;  PROGRAM:  VFILER
;  VERSION:  3.0
;  DATE:  26 June 84
;  AUTHOR:  Richard Conn
;  PREVIOUS VERSIONS:  2.0 (18 Sep 83), 1.8 (17 Sep 83)
;  PREVIOUS VERSIONS:  1.7 (9 Sep 83), 1.6 (18 Aug 83), 1.5 (20 July 83)
;  PREVIOUS VERSIONS:  1.4 (19 July 83), 1.3 (18 July 83)
;  PREVIOUS VERSIONS:  1.2 (18 July 83), 1.1 (17 July 83), 1.0 (16 July 83)
;  DERIVATION:  From FILER (Version 1.6) by Richard Conn
;		FILER from DISK7, Version 7.6C, by Frank Gaude'
;
z3env	equ	0f400h
VERS	EQU	30		;version number

; VFILER is copyright (c) 1983, 1984 by Richard Conn
; All Rights Reserved
; VFILER may be used freely by the ZCPR3 Community

; VFILER is a screen-oriented, ZCPR3-specific file utility.  It can not be
; installed to run under conventional CP/M.  VFILER
; extensively employs cursor addressing to position a pointer on the
; screen, allow the user to manipulate the pointer (up, down, right, left,
; next screen, previous screen, GOTO file).  The pointer points to files
; in the current user directory and displays the user's position dynamically
; on the screen.  Once pointing to a file, user commands can be used to
; manipulate the file (delete, copy, view on console, print on printer, tag
; for later copy or delete, and untag).  In the way of being ZCPR3-specific,
; VFILER can chain to external programs via the MCL and then return (ala
; MENU), and it recognizes Named Directories (so the user can log into B:, B4:,
; and MYDIR:, for example).

; VFILER is installed by Z3INS.

; VFILER works with ZCPR3 only, with 32k or more of RAM.  File copy
; functions are faster with large amounts of RAM.

; VFILER can be assembled for use with a Z80 or 8080 microprocessor.

;
;  SYSLIB, Z3LIB, and VLIB References
;
	ext	z3vinit,cls,gotoxy,ereol,envptr,getcrt,getprt,vprint
	ext	z3log,retud
	ext	fi0$open,f0$get,dutdir
	ext	sksp,putcl,getmdisk
	ext	cin,cout,caps,lout,crlf,lcrlf,pafdc,phlfdc
	ext	pfind,dnscan
	ext	getsh,getcl1,qshell,shpush,getzrun,putzex,putcst
	ext	getefcb,shpop,getsh2,getfn2,getshm,putshm
	ext	codend

;
;  Basic Definitions
;
TRUE	 EQU	0FFH		;define true and..
FALSE	 EQU	0       	;..false.
;
DIM	 EQU	1		;GOTO DIM
BRIGHT	 EQU	2		;GOTO BRIGHT

;
; User-Customized Definition
;
VFNAME	 MACRO			;;Name of VFILER
	 DB	'VFILER'
	 ENDM
VFNFILL	 MACRO			;;Spaces to fill out name to 8 chars
	 DB	'  '
	 ENDM
;
Z80	 EQU	TRUE 		;TRUE to use Z80 Instructions
BIHELP	 EQU	TRUE 		;TRUE to provide built-in help info
WARMBOOT EQU	FALSE		;set TRUE to warmboot on exit
DEFALPHA EQU	TRUE		;set TRUE to alpha by name and type, FALSE for
				; ... type and name by default
EPS	 EQU	18*4		;N lines x 4 cols per screen
				;  EPS = Entries Per Screen
TAGCH	 EQU	't'		;character used to mark tagged files

;
;  Command Line Builder Constants
;
FPESC	 EQU	'%'		;escape char
FPDISK	 EQU	'D'		;disk only (D)
FPUSER	 EQU	'U'		;user only (U)
FPFILE	 EQU	'F'		;file name only
MNOTE	 EQU	'#'		;denotes comment area in macro file
UIN1	 EQU	27H		;single quote for user input
UIN2	 EQU	22H		;double quote for user input

;
; Cursor Positioning Addresses
;
EPSLINE	EQU	(EPS/4)+4	;position of last line of EPS
BANADR	EQU	1*256+24	;banner address
CURHOME	EQU	3*256+1		;home address of cursor
BOTADR	EQU	24*256+1	;bottom of screen
CPMADR	EQU	EPSLINE*256+1	;command prompt message
CPADR	EQU	EPSLINE*256+22	;command prompt
ERADR	EQU	(EPSLINE+1)*256+15	;error message
FSADR	EQU	ERADR		;file size message
FNADR	EQU	1*256+62	;address of current file name
DUADR	EQU	1*256+4		;address of current DU

;
; System Functions
;
RDCON	EQU	1
WRCON	EQU	2
PUNCH	EQU	4
LIST	EQU	5
DIRCON	EQU	6
RDBUF	EQU	10
CONST	EQU	11
RESETDK	EQU	13
LOGIN	EQU	14
OPEN	EQU	15
CLOSE	EQU	16
SRCHF	EQU	17
SRCHN	EQU	18
ERASE	EQU	19
READ	EQU	20
WRITE	EQU	21
MAKE	EQU	22
REN	EQU	23
INQDISK	EQU	25
SETDMA	EQU	26
INQALC	EQU	27
ATTR	EQU	30
GETPARM	EQU	31
SGUSER	EQU	32
COMPSZ	EQU	35

;
; System Addresses
;
OS$BASE EQU	000H		;system base..
CCP	EQU	800H		;..and 'ccp' length in bytes.
GET	EQU	0FFH		;get user area e-reg value
BDOS	EQU	OS$BASE+05H
FCB	EQU	OS$BASE+5CH
FCBEXT	EQU	FCB+12
FCBRNO	EQU	FCB+32
FCB2	EQU	OS$BASE+6CH
TBUF	EQU	OS$BASE+80H
TPA	EQU	OS$BASE+100H

;
; ASCII Definitions
;
CTRLC	EQU	'C'-'@'		;..control-C..
CTRLD	EQU	'D'-'@'
CTRLE	EQU	'E'-'@'
CTRLR	EQU	'R'-'@'
CTRLS	EQU	'S'-'@'		;..XOFF..
CTRLX	EQU	'X'-'@'
BS	EQU	08H		;..backspace..
TAB	EQU	09H		;..tab..
LF	EQU	0AH		;..linefeed..
FF	EQU	0CH		;..formfeed..
CR	EQU	0DH		;..carriage return..
CAN	EQU	18H		;..cancel..
EOFCHAR	EQU	1AH		;..end-of-file..
CTRLZ	EQU	1AH		;..clear screen..
ESC	EQU	1BH		;..and escape character.

;
; MACROS TO PROVIDE Z80 EXTENSIONS
;   MACROS INCLUDE:
;
;	BR	- JUMP RELATIVE
;	BRC	- JUMP RELATIVE IF CARRY
;	BRNC	- JUMP RELATIVE IF NO CARRY
;	BRZ	- JUMP RELATIVE IF ZERO
;	BRNZ	- JUMP RELATIVE IF NO ZERO
;	BJNZ	- DECREMENT B AND JUMP RELATIVE IF NO ZERO
;
BR	MACRO	?N	;;JUMP RELATIVE
	IF	Z80
	 .z80
	 jr	?N
	 .8080
	ELSE
	 jmp	?N
	ENDIF
	ENDM
;
BRC	MACRO	?N	;;JUMP RELATIVE ON CARRY
	IF	Z80
	 .z80
	 jr	c,?N
	 .8080
	ELSE
	 jc	?N
	ENDIF
	ENDM
;
BRNC	MACRO	?N	;;JUMP RELATIVE ON NO CARRY
	IF	Z80
	 .z80
	 jr	nc,?N
	 .8080
	ELSE
	 jnc	?N
	ENDIF
	ENDM
;
BRZ	MACRO	?N	;;JUMP RELATIVE ON ZERO
	IF	Z80
	 .z80
	 jr	z,?N
	 .8080
	ELSE
	 jz	?N
	ENDIF
	ENDM
;
BRNZ	MACRO	?N	;;JUMP RELATIVE ON NO ZERO
	IF	Z80
	 .z80
	 jr	nz,?N
	 .8080
	ELSE
	 jnz	?N
	ENDIF
	ENDM
;
BJNZ	MACRO	?N	;;DECREMENT B AND JUMP RELATIVE ON NO ZERO
	IF	Z80
	 .z80
	 djnz	?N
	 .8080
	ELSE
	 dcr	b
	 jnz	?N
	ENDIF
	ENDM
;
; END OF Z80 MACRO EXTENSIONS
;

;
; Environment Definition
;
	if	z3env ne 0
;
; External ZCPR3 Environment Descriptor
;
	jmp	start
	db	'Z3ENV'	;This is a ZCPR3 Utility
	db	1	;External Environment Descriptor
z3eadr:
	dw	z3env
start:
	lhld	z3eadr	;pt to ZCPR3 environment
;
	else
;
; Internal ZCPR3 Environment Descriptor
;
	MACLIB	Z3BASE.LIB
	MACLIB	SYSENV.LIB
z3eadr:
	jmp	start
	SYSENV
start:
	lxi	h,z3eadr	;pt to ZCPR3 environment
	endif

;
; Start of Program -- Initialize ZCPR3 Environment
;  Once Environment is Initialized, One of Three Major Functions
;  will be Performed:
;	1.  VFILER will be installed as a Shell if invoked explicitly
;		by user command
;	2.  The VFILER Function will be performed if VFILER is invoked
;		by ZCPR3 as a Shell and ZEX is not Running
;	3.  A Command Line will be Input by VFILER from ZEX and Passed
;		on to ZCPR3 without Processing
;
	call	z3vinit	;initialize the ZCPR3 Env and the VLIB Env
;
;  Set Opsys Stack Pointer
;
	if	not WARMBOOT
	lxi	h,0		;clear hl-pair then..
	dad	sp		;..add stack address.
	shld	stack
	endif			;not warmboot

;
; Check to see if VFILER was executed as a shell
;
	call	qshell		;get and test message from ZCPR3
	jz	runsh		;execute shell procedures
;
; Initialize VFILER as a Shell
;
	call	shtest1		;there must be a shell stack
	call	shtest2		;there must be a command line buffer
;
; FUNCTION 1:  Set Up VFILER as a Shell
;  Run Shell Via Exit to Opsys
;
	call	getefcb		;determine name of program
	brz	shgo1		;name not given, so use default
	inx	h		;pt to name
	lxi	d,filercmd	;define name of program
	mvi	b,8
	call	moveb		;copy name
shgo1:
	call	idu		;set initial disk/user
	lxi	h,filercmd	;establish shell
	call	shpush		;push onto shell stack
	brnz	shgo2		;error?
;
;  Establish File Selection
;
	call	getfn2		;setup file name
	lxi	d,11*3		;use 4th system file
	dad	d
	lxi	d,fcb+1		;pt to FCB
	xchg
	mvi	b,11		;copy 11 chars
	call	moveb
	lxi	h,joker		;make wild if FCB is empty
	mvi	b,11		;11 chars
	ldax	d		;check first char
	cpi	' '
	cz	moveb
;
;  Print Done Message
;
	call	vprint
	db	' Shell Installed',0
	mvi	b,0		;shell message 0
	mvi	a,0		;no wait
	call	putshm		;set shell message
	jmp	chain2		;set IDU into command line and go
;
;  Error in Shell Stack Installation
;
shgo2:
	cpi	2		;shell stack full
	brnz	shgo3
	call	vprint
	db	' Shell Stack Full',0
	ret
shgo3:
	call	vprint
	db	' Shell Entry Size',0
	ret

;
;  VFILER was invoked as a Shell
;    Check for ZEX Input in Progress
;
runsh:
	call	getzrun		;check ZEX message byte
	jnz	zexrun		;process ZEX command line if ZEX running
;
; FUNCTION 2: Run VFILER and Perform Main Function
;
	mvi	a,0		;set no shell message
	call	putcst
	call	setup		;init buffers and pointers
	call	stackset	;setup stack
;
;  Process Initial Help if Available and Requested
;
	if	BIHELP		;built-in help available?
	lda	fcb+1		;check for initial help
	cpi	'/'
	brnz	runsh1
	call	helpmsg		;print help message
	call	sak		;strike any key to continue
	br	runsh2
runsh1:
	endif		;BIHELP
;
;  Check for Delay Before Resuming VFILER and Delay if Set
;
	mvi	b,0
	call	getshm		;get shell message 0
	ani	80h		;check MSB
	cnz	sak		;pause for input
;
;  Begin VFILER Processing
;
runsh2:
	call	stackset	;reset stack
	call	fload		;load files
	push	psw
	call	setscr		;set up screen display variables
	pop	psw
	brnz	runsh4		;continue if OK
;
;  Entry Point to Log Into New Dir and Resume
;
runsh3:
	call	logdir		;error - log into another dir
	br	runsh2
;
;  Entry Point for Command Processing
;    Display Screen and Input/Process Command
;
runsh4:
	call	stackset	;reset stack
	call	refresh		;refresh screen display
;
;  Display Current File and Input/Process Command
;
loopfn:
	call	loopfn1		;print current file name
;
;  Input/Process Command
;
loop:
	call	stackset	;reset stack
	call	loopfn2		;get command from user
	call	loopfn3		;process command in A
	br	loop		;continue
;
;  Set Stack Pointer
;
stackset:
	pop	d		;get return address
	lhld	crctbl		;top of stack
	sphl			;start local stack
	push	d		;return address on new stack
	ret
;
; Check for Presence of Shell Stack
;
shtest1:
	call	getsh		;get shell stack data
	rnz
	pop	psw		;clear stack
	call	vprint
	db	'No Shell Stack',0
	ret

;
; Check for Command Line
;
shtest2:
	call	getcl1		;get command line data
	rnz
	pop	psw		;clear stack
	call	vprint
	db	'No Cmd Line',0
	ret

;
; FUNCTION 3: Run ZEX on Top of VFILER
;  Accept Command Line and Pass it to ZCPR3
;
zexrun:
	call	vprint		;print prompt
	db	'VFILER> ',0
	mvi	a,1		;tell ZEX that it is prompted
	call	putzex
	call	getcl1		;pt to command line buffer
	mov	a,l		;set ptr to first char
	adi	4
	mov	c,a
	mov	a,h
	aci	0
	mov	b,a		;BC pts to first char
	mov	m,c		;store low
	inx	h
	mov	m,b		;store high
	inx	h		;pt to char count
	xchg			;... in DE
	mvi	c,rdbuf		;input line via BDOS
	push	d		;save ptr
	call	bdos
	pop	h		;pt to char count
	inx	h
	mov	e,m		;get char count
	inx	h		;pt to first char
	push	h		;save ptr
	mvi	d,0		;DE=char count
	dad	d
	xra	a		;A=0
	mov	m,a		;store ending 0
	pop	h		;pt to first char
	call	putzex		;resume ZEX (A=0)
	call	putcst		;set command status to normal (A=0)
	ret			;return to opsys

;
;**************************************************
;
; CRT Routine for VFILER
;
VCLS:
	CALL	CLS		;try to clear the screen
	RNZ			;OK if done
	PUSH	H		;save regs
	PUSH	B
	CALL	GETCRT		;get CRT Data
	INX	H		;get number of lines on screen
	MOV	B,M		;B=number of lines
VCLS1:
	CALL	CRLF		;new line
	BJNZ	VCLS1
	POP	B		;restore regs
	POP	H
	RET
;
; EREOL Routine for VFILER
;
VEREOL:
	CALL	EREOL		;try to erase to EOL
	RNZ			;OK if done
	PUSH	B		;save count
	MVI	A,' '		;space out
	CALL	VEREOL1		;send B spaces
	POP	B		;get count
	MVI	A,BS		;backspace in
VEREOL1:
	CALL	COUT		;send char
	BJNZ	VEREOL1		;count down
	RET
;
;  Setup Screen Display Variables
;
SETSCR:
	LXI	H,CURHOME	;set cursor home
	SHLD	CURAT
	LHLD	RING		;set ring position
;
;  Entry to Reset Ring Position at HL
;
SETSCR1:
	SHLD	RINGPOS
;
;  Entry to Reset Local Ring Position at HL
;
SETSCR2:
	SHLD	LOCBEG		;front of ring
	LXI	D,EPS*13	;new end?
	DAD	D
	XCHG
	LHLD	RINGEND		;end of ring
	XCHG
	CALL	CMPDEHL
	BRC	SETSCR3
	XCHG
SETSCR3:
	XCHG
	SHLD	LOCEND
	RET

;
; Display file name of current file
;
LOOPFN1:
	LXI	H,FNADR		;position cursor for file name print
	CALL	GOTOXY
	LHLD	RINGPOS		;pt to current file name
	INX	H		;pt to first char
	JMP	PRFN		;print file name
;
; Get command from user and clear error line if any
;
LOOPFN2:
	CALL	ATCMD		;position at command prompt
	CALL	DKEYIN		;wait for character from keyboard
	PUSH	PSW		;save command
	LDA	ERMFLG		;error message?
	ORA	A		;0=no
	CNZ	ERCLR		;erase old error message
	POP	PSW		;get command
	RET
;
; Process Command
;
LOOPFN3:
	CPI	MNOTE		;command summary
	BRZ	CPROCM
	CPI	'0'		;macro?
	BRC	CPROC0
	CPI	'9'+1		;macro?
	BRNC	CPROC0
CPROCM:
	CALL	CMACRO		;process macro command
	MOV	B,A		;save command
	CALL	ERMSG
	DB	'No U Fct ',0
	MOV	A,B		;get macro digit
	JMP	COUT		;return for loop processing
CPROC0:
	CALL	CTPROC		;process command or return if not found
	CALL	ERMSG
	DB	'Invld Cmd: ',0
	MOV	A,B		;get char
	CPI	' '		;expand if less than space
	BRNC	CPROC1
	MVI	A,'^'		;control
	CALL	COUT
	MOV	A,B		;get byte
	ADI	'@'		;convert to letter
CPROC1:
	JMP	COUT		;return for loop processing

;
; SET UP BUFFERS
;
SETUP:
	CALL	RETUD		;get home DU
	MOV	A,B
	STA	H$DR		;home drive
	MOV	A,C
	STA	H$U$A		;home user area
	CALL	CODEND		;start of free space
	LXI	D,100H		;256 bytes/unit
	DAD	D
	SHLD	CRCTBL		;table for CRC list et al
	DAD	D
	DAD	D		;allow 512 bytes
	SHLD	CMDBUF		;command line buffer
	MVI	M,64		;64 bytes in line
	LXI	D,70		;arbitrary beyond EOL
	DAD	D
	SHLD	DUM$FCB		;dummy FCB
	INR	H
	MVI	L,0		;next page
	SHLD	BUFENTRY	;free space to end of TPA

;
;  Set CRT/Printer Data
;
	CALL	GETCRT		;CRT DATA
	MOV	A,M
	STA	CWIDTH		;WIDTH
	INX	H
	MOV	B,M
	INX	H
	MOV	A,M
	STA	CTPP		;TEXT
	MOV	C,A
	MOV	A,B		;TOTAL NUMBER OF LINES
	SUB	C
	DCR	A		;1 LESS
	STA	CSPP		;LINES TO SKIP
	CALL	GETPRT		;PRINTER DATA
	MOV	A,M
	STA	LWIDTH		;WIDTH
	INX	H
	MOV	B,M
	INX	H
	MOV	A,M
	STA	LTPP		;TEXT
	MOV	C,A
	MOV	A,B		;TOTAL NUMBER OF LINES
	SUB	C
	STA	LSPP		;LINES TO SKIP
	INX	H
	MOV	A,M
	STA	LFFEED		;CAN FORM FEED?

;
;  Begin Further Inits
;
	XRA	A
	STA	HELPDISP	;no help display yet
;
	MVI	A,DEFALPHA	;alpha by name and type
	STA	ALPHA
;
	LHLD	ENVPTR		;pt to ZCPR3 Env Desc
	LXI	D,80H+10H	;pt to cursor commands
	DAD	D
	LXI	D,CTABLE	;pt to area
	MVI	B,4		;4 commands
CURINIT:
	MOV	A,M		;get command
	STAX	D		;put it
	INX	H		;pt to next
	INX	D
	INX	D
	INX	D
	BJNZ	CURINIT
;
	CALL	IDU		;set initial disk/user
	CALL	LOGRDU		;log into it
;
	LHLD	BUFENTRY	;base address
	SHLD	RING		;beginning of ring
;
	LXI	H,CURHOME	;home address for cursor
	SHLD	CURAT
;
	CALL	GETMDISK	;get max disk number (1-N)
	ADI	'@'		;convert to ASCII (A-P)
	STA	MAXDR		;set letter
;
	RET

;  set initial disk/user
IDU:
	LXI	D,FCB		;log into DU specification
	CALL	Z3LOG		;perform login
IDU1:
	CALL	RETUD		;get current DU in BC
	MOV	A,C		;get current user
	CALL	IDU$USET	;set it
	MOV	A,B		;get current disk
	CALL	IDU$DSET	;set it
	RET
IDU$USET:
	STA	C$U$A		;..store as current and..
	STA	O$USR		;..as original for exit.
	STA	R$U$A		;..requested user area
	RET
IDU$DSET:
	STA	C$DR
	STA	R$DR		;requested disk
	RET

; check for existance of file whose first 12 FCB bytes are pted to by DE
;   return with A=0 if not found, A=0FFH if found

FILECHK:
	LXI	H,S$FCB		;copy into FCB
	XCHG
	MVI	B,12		;12 bytes
	CALL	MOVE		;copied into S$FCB
	XCHG			;HL pts to FCB
	CALL	INITFCB		;init FCB
	LXI	D,S$FCB		;pt to FCB
	JMP	FFIND

;
; Select drive and user area (system reset for disk change on-the-fly)
;
LOGDIR:
	CALL	CPRMPT		;prompt to get drive/user selection
	DB	'Login DIR (X<cr> to Exit): ',0
	CALL	DEF$D$U
LOG1:
	CALL	LOGRDU		;set current and log in
	JMP	CRLF		;new line and exit
;
; Actually log into DU requested
;
LOGRDU:
	LDA	R$DR		;make requested disk current
	STA	C$DR
	CALL	RESET		;reset disk system
	LDA	R$U$A		;establish requested area..
	STA	C$U$A		;..as current area.
	CALL	SET$USR
	LXI	H,0		;initialize tagged..
	SHLD	TAG$TOT		;..file size accumulator.
	RET
;
; Define current drive and user area with full error trapping.
; (Check validity of user area entry first, then drive validity, then proceed)
;
DEF$D$U:
	LHLD	CMDBUF		;pt to command line buffer
	INX	H
	INX	H
	MVI	B,20		; # of blanks to..
	CALL	FILL		;..clear 'cmdbuf'.
	LHLD	CMDBUF		;get DU selection from..
	XCHG
	MVI	C,RDBUF		;..console buffer read.
	CALL	BDOS
	CALL	CONVERT		;make sure alpha is upper case
	LHLD	CMDBUF		;pt to possible drive
	INX	H
	INX	H
	CALL	SKSP		;skip over leading spaces
	MOV	A,M		;check for no input
	ORA	A
	JZ	LOOP
	MOV	A,M		;check for exit
	CALL	CAPS
	CPI	'X'
	BRNZ	DEFDU1
	INX	H		;check for space or EOL
	MOV	A,M
	DCX	H
	ANI	7FH		;mask MSB
	CPI	' '+1
	JC	OS$CCP		;abort
DEFDU1:
	CALL	DNSCAN		;look for DU or DIR form and return DU
	BRZ	ERRET		;error
	MOV	A,B		;return disk and user
	STA	R$DR
	INR	A		;set FCB
	STA	FCB
	MOV	A,C
	STA	R$U$A
	RET
;
; Error return and recovery from command cancellation
;
ERRET:
	CALL	ERMSG
	DB	'DIR Entry Error',0
	JMP	LOOP
COMCAN:
	LDA	CANFLG		;check for nature of abort
	ORA	A		;..from..
	JZ	RUNSH2		;no file found, so run the shell again
	JMP	RUNSH4		;..error/command abort.
;
; find file along path (file FCB pted to by DE)
;   on return, A=0FFH if found, A=0 if not found, and flags set
;
FFIND:
	MVI	A,0FFH		;look in current dir also
	JMP	PFIND		;path search from Z3LIB
; log in default directory
DLOGIN:
	LDA	C$DR		;disk in B
	MOV	B,A
	LDA	C$U$A		;user in C
	MOV	C,A		;fall thru to SLOGIN
; log in DU in BC
SLOGIN:
	PUSH	H		;save regs
	PUSH	D
	PUSH	B
	MOV	A,C		;set user
	CALL	SET$USR
	POP	B
	MOV	A,B		;set disk
	CALL	SET$DR
	POP	D		;restore regs
	POP	H
	RET

; e x i t

; return to ccp

;
; Entry point for VFILER exit
;
OS$CCP:
	CALL	SHPOP		;clear shell stack
;
; Entry point for command line exec
;
OS$CCP1:
	LDA	O$USR		;get and set original..
	CALL	SET$USR		;..user area and..
	LXI	D,TBUF		;..tidy up..
	MVI	C,SETDMA	;..before going home.
	CALL	BDOS

	 IF WARMBOOT
	JMP	OS$BASE
	 ENDIF			;warmboot

	 IF	NOT WARMBOOT
	LHLD	STACK		;put pointer..
	SPHL			;..back to 'sp'.
	RET			;return to ccp
	 ENDIF			;not warmboot

;
;  FLOAD loads the files into the buffer, setting up the ring
;  Return with NZ if load OK, Z if no files loaded
;
FLOAD:
	CALL	FRESTOR		;get bytes remaining on drive (decode default)
;
;  Set up file name from System File 4
;    Select all files if no entry in System File 4
;
	CALL	GETFN2		;pt to first system file name
	LXI	D,11*3		;pt to 4th file name
	DAD	D
	LXI	D,FCB+1		;pt to FCB
	MVI	B,11		;11 bytes
	CALL	MOVEB
	LXI	H,JOKER		;..treat as '*.*' with 'joker'..
	MVI	B,11		; # of characters to move
	LDAX	D		;get first char of file name
	CPI	' '		;if space, fill with *.*
	CZ	MOVEB		;set field to *.*
	LDAX	D		;get first char of file name
	CPI	'/'		;if opt, fill with *.*
	CZ	MOVEB
;
; Build ring with filename positioned in default FCB area
;
	MVI	C,SETDMA	;initialize dma address..
	LXI	D,TBUF		;..to default buffer.
	CALL	BDOS
	XRA	A		;clear search 'fcb'..
	STA	FCBEXT		;extent byte..
	STA	FCBRNO		;..and record number.
	LXI	D,FCB		;default FCB for search
	CMA
	STA	CANFLG		;make cancel flag true
	MVI	C,SRCHF		;..of first occurrence.
	CALL	BDOS
	INR	A		; 0ffh --> 00h if no file found
	BRNZ	SETRING		;if found, branch and build ring.
	STA	CANFLG		;make log-cancel toggle false (0)
	CALL	ERMSG		;else say none found, fall thru to log.
	DB	'No File Found',0
	XRA	A		;Set Error Code
	RET

; establish ring (circular list) of filenames

SETRING:
	LHLD	RING		;initialize ring pointer
	SHLD	RINGPOS		;start --> current position of ring

; put each found name in ring.  a-reg --> offset into 'tbuf' name storage

TO$RING:
	DCR	A		;un-do 'inr' from above and below
	ADD	A		;times 32 --> position index
 	ADD	A
	ADD	A
	ADD	A
	ADD	A
	ADI	TBUF		;add page offset and..
	MOV	L,A		;..put address into..
	MVI	H,0		;..hl-pair.
	LDA	FCB		;get drive/user designator and..
	MOV	M,A		;..put into 'fcb' buffer.
	XCHG
	LHLD	RINGPOS		;pointer to current load point in ring
	XCHG
	MVI	B,12		;move drive designator and name to ring
	CALL	MOVE
	XCHG			;de-pair contains next load point address
	MVI	M,' '		;space for potential..
	INX	H		;..tagging of files for mass copy.
	SHLD	RINGPOS		;store and search..
	MVI	C,SRCHN		;..for next occurrence.
	LXI	D,FCB		;filename address field
	CALL	BDOS
	INR	A		;if all done, 0ffh --> 00h.
	BRNZ	TO$RING		;if not, put next name into ring.
;
; All filenames in ring -- setup ring size and copy-buffer start point
;
	LHLD	RINGPOS		;next load point of ring is start of buffer
	SHLD	RINGEND		;set ring end..
	SHLD	BUFSTART	;..and copy-buffer start.
	PUSH	H
	LHLD	RING
	LXI	D,13		;compare 'ringend' (tab base+13)
	DAD	D
	XCHG
	POP	H
	CALL	CMPDEHL
	BRNZ	SORT		;sort if more than one file in ring
	XRA	A		;set NZ for Return
	DCR	A
	RET
;
; Sort ring of filenames
;
SORT:
	LHLD	RING		;initialize 'i' sort variable and..
	SHLD	RINGI
	LXI	D,13		;..also 'j' variable.
	DAD	D
	SHLD	RINGJ
SORTLP:
	LHLD	RINGJ		;compare names 'i & j'
	XCHG
	LHLD	RINGI
	PUSH	H		;save position pointers..
	PUSH	D		;..for potential swap.
	LDA	ALPHA		;check for type of alphabetization
	ORA	A		;if Zero, alpha by type and name
	JZ	SORTTN

; sort by file name and type
	MVI	B,12		; # of characters to compare
	CALL	CMPSTR		;do comparison
	BR	NOCMP		;final test

; sort by file type and name
SORTTN:
	PUSH	H		;save ptrs
	PUSH	D
	LXI	B,9		;pt to type
	DAD	B
	XCHG
	DAD	B
	XCHG
	MVI	B,3		;3 chars in file type
	CALL	CMPSTR		;compare type
	POP	D		;get ptrs
	POP	H
	BRNZ	NOCMP		;final test
	PUSH	H
	PUSH	D
	MVI	B,8		;8 chars in file name
	INX	H		;pt to first
	INX	D
	CALL	CMPSTR		;compare name
	POP	D		;get ptrs
	POP	H
	BRNZ	NOCMP		;final test
	LDAX	D		;final compare and fall thru
	CMP	M

; final test for swapping purposes
NOCMP:
	POP	D
	POP	H
	MVI	B,13
	BRNC	NOSWAP
;
; Swap if 'j' string larger than 'i'
;
SWAP:
	MOV	C,M		;get character from one string..
	LDAX	D		;..and one from other string.
	MOV	M,A		;second into first
	MOV	A,C		;first into second
	STAX	D
	INX	H		;bump swap pointers
	INX	D
	BJNZ	SWAP
NOSWAP:
	LHLD	RINGJ		;increment 'j' pointer
	LXI	D,13
	DAD	D
	SHLD	RINGJ
	XCHG			;see if end of 'j' loop
	LHLD	RINGEND
	CALL	CMPDEHL
	JNZ	SORTLP		;no, so more 'j' looping.
	LHLD	RINGI		;bump 'i' pointer
	LXI	D,13
	DAD	D
	SHLD	RINGI
	DAD	D		;set start over 'j' pointer
	SHLD	RINGJ
	XCHG			;see if end of 'i' loop
	LHLD	RINGEND
	CALL	CMPDEHL
	JNZ	SORTLP		;must be more 'i' loop to do
;
; Sort done -- initialize tables for fast crc calculations
;
	CALL	INITCRC
;
; Calculate buffer maximum available record capacity
;
B$SIZE:
	LXI	B,0		;count records
	LHLD	BDOS+1		;get 'bdos' entry (fbase)

	 IF	NOT WARMBOOT
	LXI	D,-(CCP)
	DAD	D
	 ENDIF			;not warmboot

	DCX	H
	XCHG			;de-pair --> highest address of buffer
	LHLD	BUFSTART	;start address of buffer (end of ring list)
B$SIZE2:
	INX	B		;increase record count by one
	PUSH	D
	LXI	D,128		; 128-byte record
	DAD	D		;buffer address + record size
	POP	D
	CALL	CMPDEHL		;compare for all done
	BRNC	B$SIZE2		;more will fit?
	DCX	B		;set maximum record count less one
	MOV	A,B		;memory available for copy?
	ORA	C
	BRNZ	B$SIZE3		;yes, buffer memory space available.
	CALL	ERMSG
	DB	'No Room',0
	XRA	A		;Error code
	RET
B$SIZE3:
	MOV	L,C		;store..
	MOV	H,B		;..maximum..
	SHLD	REC$MAX		;..record count.
	XRA	A		;return NZ for OK
	DCR	A
	RET
;
; left to right compare of two strings (de-pair points to 'a' string;
; hl-pair, to 'b'; b-reg contains string length.)
;
CMPSTR:
	LDAX	D		;get an 'a' string character and..
	CMP	M		;..check against 'b' string character.
	RNZ			;if not equal, set flag.
	INX	H		;bump compare..
	INX	D		;..pointers and..
	DCR	B		; (if compare, set as equal.)
	BRNZ	CMPSTR		;..do next character.
	RET
;
; COMMAND: MACRO (0-9)
; Process macro command
;
CMACRO:
	PUSH	PSW		;save digit
	LXI	H,MACFCB	;set up name of macro file
	LXI	D,S$FCB
	PUSH	D
	MVI	B,12		;12 chars
	CALL	MOVE
	XCHG
	CALL	INITFCB		;init FCB
	POP	D		;pt to FCB
	CALL	FFIND		;search along path for file
	BRNZ	CMFOUND		;file found
	POP	PSW		;clear stack
	POP	D		;clear ret address
	CALL	ERMSG
	DB	'File ',0
	LXI	H,MACFCB+1
	CALL	PRFN		;print file name
	CALL	VPRINT
	DB	' NOT Found',0
	JMP	LOOP
; found macro file
CMFOUND:
	CALL	SLOGIN		;log in
	LXI	D,S$FCB		;pt to FCB
	CALL	FI0$OPEN	;open for byte-oriented input
	POP	PSW		;get command
	MOV	C,A		;... in C
; new line
CMFL1:
	CALL	F0$GET		;get first char of line
	JNZ	CMFLERR		;EOF encountered
	CMP	C		;match?
	BRZ	CMFL3
; skip out line
CMFL2:
	CALL	F0$GET		;skip out line
	JNZ	CMFLERR
	CPI	LF		;EOL?
	BRNZ	CMFL2
	BR	CMFL1
; found command
CMFL3:
	MVI	A,MNOTE		;macro note?
	CMP	C
	JZ	CMFLNOTE
	PUSH	D
	LHLD	CRCTBL	;buffer for command line
	LXI	D,256	;+256
	DAD	D
	POP	D
; skip leading blanks in command line
CMFL4:
	CALL	F0$GET		;get next char
	BRNZ	CMFL6		;mark end of buffer and go
	CPI	' '		;skip leading blanks
	BRZ	CMFL4
; load command line into buffer
CMFL5:
	ANI	7FH		;mask char
	CPI	CR		;EOL?
	BRZ	CMFL6
	CPI	CTRLZ		;EOF?
	BRZ	CMFL6
	CPI	FPESC		;file pted to?
	JZ	CMFLIF
	CPI	UIN1		;user input?
	BRZ	CMFL5A
	CPI	UIN2
	BRZ	CMFL5A
CMFL50:
	MOV	M,A		;store char
	INX	H		;pt to next
CMFL51:
	CALL	F0$GET		;get next char
	BRZ	CMFL5
	BR	CMFL6

; print prompt to user and get input
CMFL5A:
	PUSH	H		;save buffer ptr
	CALL	CPRMPT		;at command prompt line
	DB	0
CMFL5B:
	CALL	F0$GET		;get next char
	BRNZ	CMFL5B1
	ANI	7FH		;mask
	CPI	UIN1		;end of prompt?
	BRZ	CMFL5C
	CPI	UIN2
	BRZ	CMFL5C
	CPI	CR		;end of prompt?
	BRZ	CMFL5C
	CPI	CTRLZ		;eof?
	BRZ	CMFL5C
	CALL	COUT		;send char
	BR	CMFL5B
CMFL5B1:
	MVI	A,CTRLZ		;eof
CMFL5C:
	PUSH	PSW		;save offending char
	LHLD	BUFSTART	;pt to buffer
	MVI	M,128		;128 char in line
	INX	H		;set no chars
	MVI	M,0
	DCX	H		;pt to count
	XCHG
	MVI	C,RDBUF		;get line from user
	CALL	BDOS
	LHLD	BUFSTART
	INX	H		;pt to count
	MOV	A,M		;get count
	INX	H		;pt to first char
	PUSH	H		;save ptr
	ADD	L
	MOV	L,A
	MOV	A,H
	ACI	0
	MOV	H,A		;HL pts to after last char
	MVI	M,0		;store ending zero
	POP	H		;HL pts to first char of line
	POP	PSW		;get char
	POP	D		;DE pts to next char in buffer
	MOV	B,A		;char in B
CMFL5D:
	MOV	A,M		;copy chars
	ORA	A		;zero=done
	BRZ	CMFL5E
	STAX	D		;put char
	INX	H		;pt to next
	INX	D
	BR	CMFL5D
CMFL5E:
	XCHG			;HL pts to next char in buffer
	MOV	A,B		;check offending char
	CPI	UIN1		;user input?
	BRZ	CMFL51
	CPI	UIN2
	BRNZ	CMFL5
	BR	CMFL51
; complete buffer and test for content
CMFL6:
	MVI	M,0		;store ending 0
	PUSH	D
	LHLD	CRCTBL		;pt to first char in line
	LXI	D,256
	DAD	D
	POP	D
	MOV	A,M		;get first char
	ORA	A		;empty?
	BRNZ	CMFL7
; error return
CMFLERR:
	PUSH	B
	CALL	DLOGIN		;return home
	POP	B
	MOV	A,C		;invalid command
	RET
; try to chain
CMFL7:
	PUSH	H		;save ptr
	CALL	DLOGIN		;return home
	POP	H
	CALL	CHAIN		;chain to command line pted to by HL
	JMP	LOOP

; print documentation built into the macro file
CMFLNOTE:
	CALL	VCLS		;new screen
	MVI	C,0		;set tab counter
CMFLN1:
	CALL	F0$GET		;get next char
	BRNZ	CMFLN4		;done
	ANI	7FH		;mask
	CPI	CTRLZ		;eof=done
	BRZ	CMFLN4
	CPI	TAB		;tab process
	BRZ	CMFLN2
	CALL	COUT		;print char
	CPI	CR		;new line?
	BRZ	CMFLN3
	INR	C		;new pos
	CPI	LF
	BRNZ	CMFLN1
	DCR	C		;back up position count
	BR	CMFLN1
CMFLN2:
	MVI	A,' '		;space over for tab
	CALL	COUT
	INR	C		;incr position
	MOV	A,C		;done?
	ANI	7		;every 8
	BRNZ	CMFLN2
	BR	CMFLN1
CMFLN3:
	MVI	C,0		;bol for CR
	BR	CMFLN1
CMFLN4:
	LHLD	RINGPOS		;print current file
	INX	H
	CALL	PRFN
	CALL	VPRINT
	DB	'  Enter Digit or RETURN - ',0
	CALL	DKEYIN
	POP	D		;get ret address
	CPI	'0'		;in range?
	JC	RUNSH4
	CPI	'9'+1		;in range?
	JNC	RUNSH4
	PUSH	D		;replace ret address
	JMP	CMACRO

; insert file pted to (% prefix command)
CMFLIF:
	CALL	F0$GET		;get next char
	JNZ	CMFL6
	CPI	FPESC		;doubled?
	JZ	CMFL50
	LXI	D,CMFL51	;return address
	PUSH	D		;... on stack
	ANI	5FH		;capitalize
	CPI	FPFILE		;file only?
	BRZ	CMFLIF1
	CPI	FPDISK		;disk only?
	BRZ	CMFLIF2
	CPI	FPUSER		;user only?
	BRZ	CMFLIF3
	CALL	CMFLIF2		;Disk
	CALL	CMFLIF3		;User
	MVI	M,':'		;separator
	INX	H		;fall thru to file name
CMFLIF1:
	XCHG			;save ptr to next
	LHLD	RINGPOS		;pt to current file
	INX	H
	MVI	B,8		;8 chars in filename
	CALL	CMFL5X
	MVI	A,'.'		;put dot
	STAX	D
	INX	D
	MVI	B,3		;3 chars max
	CALL	CMFL5X
	XCHG			;HL pts to next
	RET
CMFLIF2:
	LDA	C$DR		;store disk
	ADI	'A'
	MOV	M,A
	INX	H		;pt to next
	RET
CMFLIF3:
	LDA	C$U$A		;user
	CPI	10		;less than 10?
	BRC	CMFLIF32
	MVI	B,'0'		;compute 10's
CMFLIF30:
	SUI	10		;subtract 10's
	BRC	CMFLIF31
	INR	B
	BR	CMFLIF30
CMFLIF31:
	ADI	10
	MOV	M,B		;store 10's
	INX	H
CMFLIF32:
	ADI	'0'		;store 1's
	MOV	M,A
	INX	H
	RET
CMFL5X:
	MOV	A,M		;get char
	CPI	' '
	BRZ	CMFL5X1
	STAX	D		;put char
	INX	H		;pt to next
	INX	D
	BJNZ	CMFL5X
	RET
CMFL5X1:
	INX	H		;advance
	BJNZ	CMFL5X1
	RET
;
; Process command from table
;
CTPROC:
	MOV	B,A		;command in B
	LXI	H,CTABLE	;pt to table
	MOV	A,M		;any cursor commands?
	ORA	A
	JNZ	CTPR1
	LXI	H,CTAB1
;
; Command table scanner
;	HL = Table
;	B  = Command Letter
;
CTPR1:
	MOV	A,M		;get table command char
	ORA	A		;end of table?
	RZ			;done if so
	CMP	B		;match?
	BRZ	CTPR2
	INX	H		;skip to next entry
	INX	H
	INX	H
	BR	CTPR1
CTPR2:
	INX	H		;pt to address
	MOV	A,M		;get low
	INX	H
	MOV	H,M		;get high
	MOV	L,A
	XTHL			;address on stack
	RET			;"jump" to routine

; Command Table
CTABLE:
	DB	0		;user cursor positioning
	DW	UP
	DB	0
	DW	DOWN
	DB	0
	DW	FORWARD
	DB	0
	DW	REVERSE
CTAB1:
	DB	CTRLC		;if exit, then to opsys
	DW	OS$CCP
	DB	CTRLR		;screen refresh?
	DW	RUNSH4
	DB	CTRLE		;system cursor positioning
	DW	UP
	DB	CTRLX
	DW	DOWN
	DB	CTRLD
	DW	FORWARD
	DB	CTRLS
	DW	REVERSE
	DB	CR		;nop
	DW	LOOP
	DB	'+'		;jump forward
	DW	JUMPF
	DB	'-'		;jump backward
	DW	JUMPB
	DB	' '		;go forward
	DW	FORWARD
	DB	BS		;back up?
	DW	REVERSE
	DB	'A'		;alphabetize?
	DW	ALPHASW
	DB	'C'		;copy a file?
	DW	FCOPY
	DB	'D'		;delete a file?
	DW	FDEL
	DB	'F'		;show file size?
	DW	FSIZE
	DB	'G'		;group operation?
	DW	GROUP$OP
	DB	'H'		;external help?
	DW	EXTHELP
	DB	'J'		;jump to a file?
	DW	GOTO
	DB	'N'		;log-in another drive?
	DW	RUNSH3
	DB	'P'		;output file to 'list' device?
	DW	LSTFILE
	DB	'Q'		;screen refresh?
	DW	RUNSH4
	DB	'R'		;rename?
	DW	RENAME
	DB	'S'		;free bytes on..
	DW	DSTAT		;..requested drive?
	DB	'T'		;if tag, put TAGCH in..
	DW	TAG		;..front of cursor.
	DB	'U'		;remove TAGCH from..
	DW	UNTAG		;..in front of cursor?
	DB	'V'		; 'view' file at console?
	DW	VIEW
	DB	'Z'		;run ZCPR3 command
	DW	RUNZCPR3
;
	IF	BIHELP		;built-in help?
	DB	'?'		;help
	DW	HELP
	DB	'/'		;help also
	DW	HELP
	ELSE
	DB	'?'		;help
	DW	EXTHELP
	DB	'/'		;help also
	DW	EXTHELP
	ENDIF		;BIHELP
;
	DB	0		;end of table

; h e l p  (menu)

	IF	BIHELP		;built-in Help?
HELPMSG:
	CALL	VPRINT
	DB	CR,LF,CR,LF
	DB	'	'
	DB	'-- Tagging Commands --    --------- File Operat'
	DB	'ions ----------',CR,LF
	DB	'	'
	DB	'   T - ',DIM,'Tag File',BRIGHT
	DB	'            C - ',DIM,'Copy File',BRIGHT,'       '
	DB	'D - ',DIM,'Delete File',BRIGHT,CR,LF
	DB	'	'
	DB	'   U - ',DIM,'Untag File',BRIGHT
	DB	'          F - ',DIM,'File Size',BRIGHT,'       '
	DB	'R - ',DIM,'Rename File',BRIGHT,CR,LF
	DB	'	'
	DB	'                          G - ',DIM,'Group '
	DB	'Copy/Delete/FSize/Tag/Untag',BRIGHT,CR,LF
	DB	CR,LF
	DB	'                 '
	DB	'-- File Print & View --   --- User  Functions ---',CR,LF
	DB	'  -- Cursor --   '
	DB	'P - ',DIM,'Print',BRIGHT
	DB	'      V - ',DIM,'View',BRIGHT
	DB	'   0-9 - ',DIM,'Execute',BRIGHT,'  ',MNOTE,' - ',DIM,'Help'
	DB	BRIGHT,CR,LF
	DB	'       ^E        '
	DB	CR,LF
	DB	'        ^        '
	DB	'-- Movement Commands --   ---- Miscellaneous ----',CR,LF
	DB	'   ^S <-+-> ^D   '
	DB	'  <SP> - ',DIM,'File Forward'
	DB	BRIGHT,'      A - ',DIM,'Toggle Alpha Sort',BRIGHT,CR,LF
	DB	'        v        '
	DB	'  <BS> - ',DIM,'File Backward'
	DB	BRIGHT,'     H - ',DIM,'Help File',BRIGHT,CR,LF
	DB	'       ^X        '
	DB	'   +   - ',DIM,'Screen Forward',BRIGHT
	DB	'    N - ',DIM,'New DIR',BRIGHT,CR,LF
	DB	'                 '
	DB	'   -   - ',DIM,'Screen Backward',BRIGHT
	DB	'   S - ',DIM,'Disk Status',BRIGHT,CR,LF
	DB	'  -- Screen --   '
	DB	'   J   - ',DIM,'Jump to a File',BRIGHT
	DB	'    Z - ',DIM,'ZCPR3 Command',BRIGHT,CR,LF
	DB	'    ^A  ',DIM,'Left',BRIGHT,'     '
	DB	'   Q   - ',DIM,'Refresh Screen',BRIGHT
	DB	'   ^C - ',DIM,'Exit',BRIGHT,CR,LF
	DB	'    ^F  ',DIM,'Right',BRIGHT,'    '
	DB	0
	RET
HELP:
	LDA	HELPDISP	;toggle flag
	CMA
	STA	HELPDISP
	JMP	RUNSH4		;refresh screen and restart
	ENDIF		;BIHELP
;
; COMMAND: H
;
EXTHELP:
	CALL	CPRMPT
	DB	'Read Help File (Y/N)? ',0
	CALL	CIN
	CALL	CAPS
	CPI	'Y'
	JNZ	LOOPFN
	CALL	ERMSG
	DB	DIM,'Chaining to Help File ...',BRIGHT,0
	LXI	H,MOREHELP	;run HELP Command
	CALL	CHAIN		;chain to it
	JMP	LOOPFN

;
; COMMAND: A
;
; Toggle alphabetize switch and reload files
;
ALPHASW:
	LDA	ALPHA		;toggle flag
	CMA
	STA	ALPHA
	CALL	CPRMPT		;tell user what is happening
	DB	'Sorting by File ',0
	LDA	ALPHA		;get flag
	ORA	A		;check it
	BRZ	ASWTN
	CALL	VPRINT
	DB	'Name and Type',0
	JMP	RUNSH2
ASWTN:
	CALL	VPRINT
	DB	'Type and Name',0
	JMP	RUNSH2		;reload files

;
; COMMAND: Z
;
; Execute ZCPR3 command line
;
RUNZCPR3:
	CALL	ERMSG		;position at error message line
	DB	0
	LXI	H,H$DR		;pt to Home Drive (followed by Home User)
	CALL	PRDU		;print DU
	CALL	VPRINT		;print prompt
	DB	'> ',0
	PUSH	D
	LHLD	CRCTBL		;use last half of CRC Table
	LXI	D,256
	DAD	D
	POP	D
	MVI	M,126		;store length of line
	INX	H
	MVI	M,0		;store count
	DCX	H		;pt to beginning
	XCHG			;DE pts to buffer
	PUSH	D
	MVI	C,RDBUF		;Read Line from User
	CALL	BDOS
	POP	H
	INX	H		;pt to char count
	MOV	E,M
	INX	H		;pt to first char
	MVI	D,0		;set no high-order offset
	PUSH	H		;save current ptr for later
	DAD	D
	MVI	M,0		;store ending zero
	POP	H		;pt to first char of command line
	MOV	A,M		;abort if no first char
	ORA	A
	JZ	LOOP
	CALL	CHAIN		;chain to it
	JMP	LOOPFN		;continue

; chain to command pted to by HL ending in zero

CHAIN:
	CALL	PUTCL		;store command line
	BRNZ	CHAIN1
	CALL	ERMSG
	DB	'CL Err',0
	RET
CHAIN1:
	MVI	B,0		;set shell message 0
	MVI	A,80H		;enable wait
	CALL	PUTSHM
CHAIN2:
	LDA	C$DR		;get current disk
	ADI	'A'		;convert to ASCII
	STA	FILE$D		;store in proper place
	LDA	C$U$A		;get current user
	LXI	H,FILE$U	;store user number
	MVI	C,'0'		;set char
CHAIN3:
	SUI	10		;convert to ASCII
	BRC	CHAIN4
	INR	C		;increment 10's char
	BR	CHAIN3
CHAIN4:
	MOV	M,C		;store 10's digit char
	INX	H		;pt to 1's digit
	ADI	10+'0'		;add back for 1's digit
	MOV	M,A		;store 1's digit char
	CALL	GETSH2		;pt to shell buffer
	LXI	D,8		;skip 8 chars (VFILER Name)
	DAD	D
	LXI	D,FILE$D	;place DU
	XCHG
	MVI	B,5		;store 5 chars (duu:0)
	CALL	MOVEB
	JMP	OS$CCP1	;run command line

;
; COMMAND: G
;
; Group operation
;
GROUP$OP:
	CALL	CPRMPT
	DB	'Group Cmd (Copy, Delete, File Sizes, Tag, Untag)? ',0
	CALL	KEYIN		;get response
	MOV	B,A		;command in B
	LXI	H,GTABLE	;pt to table
	CALL	CTPR1		;process command
	JMP	LOOP		;resume if not found
;
; Group Command Table
;
GTABLE:
	DB	'C'
	DW	MASS$COPY
	DB	'D'
	DW	MASS$DEL
	DB	'F'
	DW	MASS$FSIZE
	DB	'T'
	DW	MASS$TAG
	DB	'U'
	DW	MASS$UNTAG
	DB	0		;end of table

;
; COMMAND: GU
;
MASS$UNTAG:
	MVI	B,' '		;untag char
;
;  Common Entry Point for Mass Untag/Tag - B = Untag/Tag Char
;
MUTLOOP:
	LHLD	RINGPOS		;move to tag
	LXI	D,12
	DAD	D
	MOV	M,B		;set tag byte
	INX	H		;pt to next
	SHLD	RINGPOS
	XCHG			;done?
	LHLD	LOCEND
	CALL	CMPDEHL
	BRNZ	MUTLOOP
	LXI	H,CURHOME	;reset cursor
	SHLD	CURAT
	LHLD	LOCBEG		;set ring position
	JMP	JFW0		;reset screen from LOCBEG

;
; COMMAND: GT
;
MASS$TAG:
	MVI	B,TAGCH		;tag char
	BR	MUTLOOP		;do work

;
; COMMAND: U
;
UNTAG:
	MVI	B,' '		;set tag byte
;
;  Common Entry Point for Untag/Tag - B = Untag/Tag Char
;
UT:
	LHLD	RINGPOS		;move back one..
	LXI	D,12		;..character position..
	DAD	D		;..and check tagging status.
	MOV	M,B		;if file previously tagged, remove..
	CALL	REFFN		;refresh file name
	JMP	FORWARD
 
;
; COMMAND: T
;
TAG:
	MVI	B,TAGCH		;tag char
	BR	UT		;do tag

;
; Refresh file name with new tag
;
REFFN:
	CALL	CLRCUR		;clear cursor
	MVI	A,' '		;one more space
	CALL	COUT
	LHLD	RINGPOS		;reprint file name
	INX	H
	CALL	PRFN
	MOV	A,M		;print tag
	JMP	COUT

;
; COMMAND: GF
;
MASS$FSIZE:
	CALL	WORKMSG		;print 'Working' msg
	LXI	H,0		;zero total
	SHLD	TAG$TOT
	LHLD	RINGPOS		;save ring position
	SHLD	SRINGPOS
	LHLD	RING		;set new ring position
	SHLD	RINGPOS
;
;  Main Loop to Compute File Sizes
;
MASS$FS:
	LHLD	RINGEND		;at end of ring?
	XCHG
	LHLD	RINGPOS
	CALL	CMPDEHL		;same if so
	BRZ	MASS$FS2
	LXI	D,12		;pt to tag
	DAD	D
	MOV	A,M		;get tag
	INX	H		;pt to next file
	CPI	TAGCH		;tagged?
	BRNZ	MASS$FS1	;skip if not tagged
;
;  Compute Size of Tagged File
;
	PUSH	H		;save ptr to next file
	CALL	SIZER		;compute file size
	XCHG			;size in DE
	LHLD	TAG$TOT		;get total
	DAD	D
	SHLD	TAG$TOT		;new total
	POP	H		;get ptr to next file
MASS$FS1:
	SHLD	RINGPOS		;set ptr to next file
	BR	MASS$FS
;
;  File Size Accumulation Done - Print Result
;
MASS$FS2:
	LHLD	SRINGPOS	;restore ring position
	SHLD	RINGPOS
	CALL	FSNOTE		;print file size message
	CALL	VPRINT		;print specific
	DB	'Tagged Files is ',0
	LHLD	TAG$TOT		;get total
	BR	FSIZE1		;print size in HL and return

;
; COMMAND: F
;
; determine and display file size in kilobytes -- round up to next disk
; allocation block -- accumulate tagged file summation
;
FSIZE:
	CALL	SIZER		;compute file size of current file
	PUSH	H		;save value
	CALL	FSNOTE		;print file size message
	LHLD	RINGPOS		;print file name
	INX	H
	CALL	PRFN
	CALL	VPRINT		;print trailer
	DB	' is ',0
	POP	H		;get size
;
;  Print file size in HL and exit to screen
;
FSIZE1:
	CALL	PHLFDC          ;print value in HL as floating
	MVI	A,'K'		;print suffix
	CALL	COUT
	JMP	LOOPFN		;resume

;
;  Compute File Size at RINGPOS
;    Return Size in HL
;
SIZER:
	CALL	RINGFCB		;move name to 's$fcb'
;
; Determine file record count and save in RCNT
;
	MVI	C,COMPSZ
	LXI	D,S$FCB
	CALL	BDOS
	LHLD	S$FCB+33
	SHLD	RCNT		;save record count and..
	LXI	H,0
	SHLD	S$FCB+33	;..reset opsys
;
; Round up to next disk allocation block
;
	LDA	B$MASK		;sectors/block - 1
	PUSH	PSW		;save 'blm'
	MOV	L,A
	XCHG
	LHLD	RCNT		;..use here.
	DAD	D		;round up to next block
	MVI	B,3+1		;convert from..
	CALL	SHIFTLP		;..records to kilobytes.
	POP	PSW		;retrieve 'blm'
	RRC			;convert..
	RRC			;..to..
	RRC			;..kilobytes/block.
	ANI	1FH
	CMA			;finish rounding
	ANA	L
	MOV	L,A		;hl-pair contains # of kilobytes
	RET

;
; COMMAND: - (Previous Screen)
;
JUMPB:
	LXI	H,CURHOME	;set cursor home
	SHLD	CURAT
	LHLD	RING		;at front?
	XCHG
	LHLD	LOCBEG
	CALL	CMPDEHL
	BRZ	JUMPBW		;back up and wrap around
	SHLD	LOCEND		;set new end
	LXI	D,-EPS*13	;back up
	DAD	D
	SHLD	LOCBEG		;new beginning
	SHLD	RINGPOS		;new position
	JMP	RUNSH4
JUMPBW:
	LHLD	LOCBEG		;at first screen?
	XCHG
	LHLD	RING		;pt to first element of ring
	CALL	CMPDEHL
	BRZ	JBW0		;advance to end
	LXI	H,-EPS*13	;back up
	DAD	D		;first element of new local ring
	BR	JFW0
JBW0:
	LXI	D,EPS*13	;pt to next screen
	DAD	D
	XCHG
	LHLD	RINGEND
	CALL	CMPDEHL
	XCHG
	BRZ	JBW1
	BRC	JBW0
JBW1:
	LXI	D,-EPS*13
	DAD	D		;pt to first element of new local ring
	BR	JFW0
;
; COMMAND: + (Next Screen)
;
JUMPF:
	LXI	H,CURHOME	;set cursor to home
	SHLD	CURAT
	LHLD	LOCEND		;see if Local End <= Ring End
	XCHG
	LHLD	RINGEND
	CALL	CMPDEHL
	BRZ	CMDLOOP
	LHLD	LOCEND		;new screen starting at LOCEND
	BR	JFW0

;
;  Reset to Beginning of RING and Resume Command Looping
;
CMDLOOP:
	CALL	SETSCR		;reset all screen pointers
CMDLRET:
	JMP	RUNSH4
;
;  Reset RING Position to HL
;
JFW0:
	CALL	SETSCR1		;reset RINGPOS on ...
	BR	CMDLRET
;
;  Reset Local Ring to HL
;
JFW0A:
	CALL	SETSCR2		;reset LOCBEG on ...
	BR	CMDLRET

;
; COMMAND: ' ', Left-Arrow
;
FORWARD:
	CALL	CLRCUR		;clear cursor
	CALL	FOR0		;position on screen and in ring
	CALL	SETCUR		;set cursor
	JMP	LOOPFN
;  advance routine
FOR0:
	LHLD	RINGPOS		;at end of loop yet?
	LXI	D,13		;i.e., will we be at end of loop?
	DAD	D
	XCHG
	LHLD	LOCEND
	CALL	CMPDEHL		;compare 'present' to 'end'
	BRNZ	FORW		;to next print position
	CALL	CUR$FIRST	;position cursor
	LHLD	LOCBEG		;set position pointer to beginning and..
	SHLD	RINGPOS
	RET
FORW:
	LHLD	RINGPOS		;advance in ring
	LXI	D,13
	DAD	D
	SHLD	RINGPOS		;new position
	CALL	CUR$NEXT	;position cursor
	RET

;
; COMMAND: BS, Right-Arrow
;
REVERSE:
	CALL	CLRCUR		;clear cursor
	CALL	REV0		;position on screen and in ring
	CALL	SETCUR		;set cursor
	JMP	LOOPFN
;  Back Up Routine
REV0:
	LHLD	LOCBEG
	XCHG
	LHLD	RINGPOS		;see if at beginning of ring
	CALL	CMPDEHL
	BRNZ	REV1		;skip position pointer reset if not..
	CALL	CUR$LAST	;end of local ring
	LHLD	LOCEND		;set to end +1 to backup to end
	LXI	D,-13
	DAD	D
	SHLD	RINGPOS
	RET
REV1:
	CALL	CUR$BACK	;back up 1
REV2:
	LHLD	RINGPOS
	LXI	D,-13		;one ring position..
	DAD	D		;..backwards.
	SHLD	RINGPOS
	RET

;
; COMMAND: Up-Arrow
;
UP:
	CALL	CLRCUR		;clear cursor
	LHLD	RINGPOS		;see if wrap around
	LXI	D,-13*4		;4 entries
	DAD	D
	XCHG
	LHLD	LOCBEG		;beginning of local screen
	CALL	CMPDEHL
	BRC	UP2		;wrap around
	MVI	B,4		;back up 4 entries
UP1:
	PUSH	B		;save count
	CALL	REV0		;back up in ring and on screen (no print)
	POP	B		;get count
	BJNZ	UP1
	BR	DOWN1A
UP2:
	LHLD	RINGPOS		;advance to beyond end
	LXI	D,13*4
	DAD	D
	XCHG
	LHLD	LOCEND		;compare to local end
	XCHG
	CALL	CMPDEHL
	BRZ	DOWN1A		;at end, so too far
	BRC	DOWN1A		;beyond end, so back up
	SHLD	RINGPOS		;new ring position
	LHLD	CURAT		;advance cursor
	INR	H		;next line
	SHLD	CURAT
	BR	UP2

;
; COMMAND: Down-Arrow
;
DOWN:
	CALL	CLRCUR		;clear cursor
	LHLD	RINGPOS		;see if wrap around
	LXI	D,13*4		;4 entries
	DAD	D
	XCHG
	LHLD	LOCEND		;end of local screen
	XCHG
	CALL	CMPDEHL
	BRZ	DOWN2		;wrap around
	BRC	DOWN2		;wrap around
	MVI	B,4		;forward 4 entries
DOWN1:
	PUSH	B		;save count
	CALL	FOR0		;advance in ring and on screen (no print)
	POP	B		;get count
	BJNZ	DOWN1
DOWN1A:
	CALL	SETCUR		;set cursor
	JMP	LOOPFN
DOWN2:
	LHLD	CURAT		;preserve column
	MOV	B,L		;column number in B
	LXI	H,CURHOME	;home position
	SHLD	CURAT		;set new position
	LHLD	LOCBEG		;beginning of local ring
	SHLD	RINGPOS		;new ring position
DOWN3:
	LHLD	CURAT		;check for at top of column
	MOV	A,L		;get col
	CMP	B		;there?
	BRZ	DOWN1A
	LHLD	RINGPOS		;advance in ring
	LXI	D,13		;13 bytes/entry
	DAD	D
	SHLD	RINGPOS
	LHLD	CURAT		;get cursor position
	LXI	D,19		;advance 19 bytes/screen entry
	DAD	D
	SHLD	CURAT
	BR	DOWN3

;
; COMMAND: S
;
; Determine remaining storage on requested disk
;
DSTAT:
	CALL	CPRMPT
	DB	'Status of Disk (Letter): ',0
	CALL	KEYIN		;get char
	PUSH	PSW
	CALL	CRLF
	POP	PSW
	SUI	'A'		;convert to number
	JC	LOOP
	MOV	B,A		;... in B
	LDA	MAXDR		;compare to max
	SUI	'A'
	CMP	B
	JC	LOOPFN
	MOV	A,B		;get disk
	STA	R$DR		;requested drive
	CALL	RESET		;..login as current.
	CALL	FRESTOR		;determine free space remaining
	CALL	PRINT$FRE	;print value
	LDA	C$DR		;login original as..
	CALL	SET$DR		;..current drive.
	JMP	LOOPFN

;
; COMMAND: GD
;
MASS$DEL:
	CALL	CPRMPT
	DB	'Mass Delete (Y/N/V=Verify)? ',0
	CALL	KEYIN		;get response
	CPI	'Y'
	BRZ	MD1
	CPI	'V'
	JNZ	LOOP		;return to position
MD1:
	STA	MDFLG		;set flag
	STA	MASSOP		;NZ=Mass Delete
	JMP	MASS$RUN

;
; COMMAND: D
;
FDEL:
	XRA	A		;set no mass operation
	STA	MFLAG
	CALL	DELETE		;delete file
;
;  Was Deletion Done?  Abort if Not
;
	LDA	DELCODE		;0=not done
	ORA	A
	JZ	LOOPFN		;abort if not
;
;  Reset Local Display
;
	LHLD	LOCEND		;move in end
	LXI	D,-13
	DAD	D
	SHLD	LOCEND
	XCHG
	LHLD	RINGPOS		;position beyond end of ring?
	CALL	CMPDEHL
	BRNZ	FDEL1
;
;  Cursor Beyond End of Display
;
	CALL	CUR$BACK	;back up cursor
	LHLD	LOCEND		;reset position
	LXI	D,-13
	DAD	D
	SHLD	RINGPOS
FDEL1:
	LHLD	LOCEND		;get local end
	XCHG			;... in DE
	LHLD	LOCBEG		;erased all local files?
	CALL	CMPDEHL
	JZ	CMDLOOP		;reset
	JMP	JFW0A		;rescreen from LOCBEG

;
;  Delete filename at RINGPOS
;
DELETE:
	XRA	A
	STA	DELCODE		;set deletion not done
	CALL	RINGFCB		;get file name
	LDA	MFLAG		;mass operation?
	ORA	A		;0=no
	BRZ	DEL2		;do delete
;
;  Test for Verify on Mass Delete
;
	LDA	MDFLG		;verify?
	CPI	'V'
	BRNZ	DEL1		;delete without verify
;
;  Verify and Delete only if Approved
;
	CALL	CPRMPT
	DB	'Delete ',0
	CALL	PRFNS		;print file name in S$FCB
	CALL	VPRINT
	DB	' (Y/N)? ',0
	CALL	KEYIN
	CPI	'Y'
	RNZ			;abort if not approved
;
; Delete File (Mass Operation)
;
DEL1:
	LDA	MDFLG		;mass operation - verify on?
	CPI	'V'
	BRZ	DEL2
	CALL	ERMSG		;print information for mass del w/o verify
	DB	'Deleting File ',0
	CALL	PRFNS
;
;  Delete File in S$FCB
;
DEL2:
	LXI	H,S$FCB		;set file to R/W
	CALL	ATTRIB
	RZ			;abort
	LXI	D,S$FCB		;point at delete 'fcb'
	MVI	C,ERASE		;erase function
	CALL	BDOS
	INR	A
	BRNZ	DELCL		;close up erased position
FNF$MSG:
	CALL	FNF1		;show error message
	JMP	LOOPFN

;
; Close up erased position
;
DELCL:
	MVI	A,0FFH
	STA	DELCODE		;set deletion done
	LHLD	RINGPOS		;prepare move up pointers
	PUSH	H
	LXI	D,13		;13 bytes/entry
	DAD	D		;de-pair = 'to' location
	POP	D		;hl-pair = 'from' location
;
;  Move 13-byte ring entries from HL to DE
;
MOVUP:
	XCHG			;HL=dest
	PUSH	H		;check if at end
	LHLD	RINGEND		;get old end pointer
	CALL	CMPDEHL		;check against current end location
	POP	H
	XCHG			;DE=dest
	BRZ	MOVDONE		;must be at end of ring
	MVI	B,13		;one name size
	CALL	MOVE		;move one name up
	BR	MOVUP		;go check end parameters
;
;  Move Complete
;
MOVDONE:
	LHLD	RING		;see if ring is empty
	XCHG
	SHLD	RINGEND		;set new ring end if all moved
	CALL	CMPDEHL		;..(listend --> listpos --> ring)
	RNZ
	CALL	ERMSG
	DB	'List Empty',0
	JMP	RUNSH3		;go to drive/user area with files

;
; COMMAND: R
;
; Set-up to rename file at cursor position -- scan keyboard buffer and
; move filename to destination FCB (DFCB)
;
RENAME:
	LHLD	RINGPOS		;move name from ring to rename 'fcb'
	LXI	D,D$FCB		;place to move name
	MVI	B,12		;amount to move
	CALL	MOVE
	CALL	CPRMPT		;new name prompt
	DB	'Rename File to: ',0
	LXI	D,D$FCB+16	;pt to FCB to fill
	CALL	FILENAME	;get file name
	LXI	H,D$FCB+1	;check for any wild cards -- none permitted
	MVI	B,11		;11 bytes
WILDCHK:
	MOV	A,M		;get char
	INX	H		;pt to next
	CPI	'?'		;wild?
	BRZ	WILDFND
	BJNZ	WILDCHK

; copy old file status bit ($r/o or $sys) to new filename

CPYBITS:
	LXI	D,D$FCB+1	;first character of old name..
	LXI	H,D$FCB+17	;..and of new name.
	MVI	B,11		; # of bytes with tag bits
CBITS1:
	LDAX	D		;fetch bit of old name character
	ANI	128		;strip upper bit and..
	MOV	C,A		;..save in b-reg.
	MVI	A,7FH		;mask for character only
	ANA	M		;put masked character into a-reg
	ORA	C		;add old bit
	MOV	M,A		;copy new byte back
	INX	H		;bump copy pointers
	INX	D
	BJNZ	CBITS1

; check if new filename already exists.  if so, say so.  then go
; to command loop without moving ring position

	LDA	D$FCB		;copy new name to source 'fcb'
	STA	S$FCB
	MVI	B,11
	LXI	H,D$FCB+17	;copy new name to..
	LXI	D,S$FCB+1	;..source 'fcb' for existence check.
	CALL	MOVE
	LXI	H,S$FCB+12	;clear 'fcb' system..
	CALL	INITFCB		;..fields.
	LXI	D,S$FCB		;search to see if this file exists
	MVI	C,SRCHF		;search first function
	CALL	BDOS
	INR	A		; 0ffh --> 00h if file not found
	BRZ	RENFILE		;to rename, if duplicate doesn't exists.
	CALL	ERMSG		;announce the situation
	DB	'File Already Exists',0
	JMP	COMCAN		;try again?

; wild char found in file name -- error

WILDFND:
	CALL	ERMSG
	DB	'AFN NOT Allowed',0
	JMP	COMCAN

; copy new name into ring position

RENFILE:
	LHLD	RINGPOS		;get ring position pointer
	INX	H		;pt to name
	PUSH	H		;save ptr
	XCHG
	LXI	H,D$FCB+17	;point at new name and..
	MVI	B,11
	CALL	MOVE		;..move.
	LHLD	CURAT		;get current position on screen
	LXI	D,4		;advance 4 chars
	DAD	D
	CALL	GOTOXY
	POP	H		;get ptr
	CALL	PRFN		;print file name
	MOV	A,M		;print tag
	CALL	COUT
	LXI	D,D$FCB		;rename 'fcb' location
	MVI	C,REN		;rename function
	CALL	BDOS
	INR	A		; 0ffh --> 00h if rename error
	JNZ	LOOP		;if okay, proceed, else..
	JMP	FNF$MSG		;..show no-file msg.

; get file name from user and process into FCB pted to by DE
FILENAME:
	PUSH	D		;save ptr
	LHLD	CMDBUF		;command line location
	XCHG
	MVI	C,RDBUF		;console read-buffer function
	CALL	BDOS
	CALL	CONVERT		;capitalize alpha
	POP	H		;set to null drive
	MVI	M,0		;..required by 'bdos'.
	INX	H

; initialize new filename field with spaces

	PUSH	H		;save start pointer
	MVI	B,11		; # of spaces to 'blank'
	CALL	FILL
	POP	H
	XCHG
	LHLD	CMDBUF		;put length..
	INX	H
	MOV	C,M		;..in c-reg.
	INX	H
	XCHG			;de-pair --> buffer pointer and hl-pair..
	CALL	UNSPACE		;..--> 'fcb' pointer.  remove leading spaces.

; extend buffer to spaces beyond command length

EXTEND:
	PUSH	H
	MOV	L,C		;double-byte remaining length
	MVI	H,0
	DAD	D		;to buffer end +1
	MVI	M,' '		;force illegal character end
	POP	H

; start filename scan

SCAN:
	MVI	B,8		; 8 characters in filename
SCAN1:
	CALL	CKLEGAL		;get and see if legal character
	JC	COMCAN		;all of command line?
	CPI	' '		;see if end of parameter field
	RZ			;rename file
	CPI	'.'		;at end of filename
	BRZ	SCAN2		;process filetype field
	CPI	'*'		;rest wild?
	BRZ	SCAN1B
	MOV	M,A		;put character into destination 'fcb'
	INX	H
	BJNZ	SCAN1

; entry if eight characters without a 'period'

SCAN1A:
	CALL	CKLEGAL		;scan buffer up to period or end
	RC			;no extent if not legal
	CPI	' '		;end of parameter field?
	RZ
	CPI	'.'
	BRNZ	SCAN1A		;do till end or period
	BR	SCAN2A		;continue at correct place

; make rest of entry wild

SCAN1B:
	MVI	M,'?'		;fill with ?'s
	INX	H
	BJNZ	SCAN1B
	LDAX	D		;get next char
	INX	D		;pt to after dot
	CPI	'.'		;must be dot
	JNZ	COMCAN		;cancel if not
	BR	SCAN2A

; build filetype field

SCAN2:
	INX	H		;advance ptr to file type field
	BJNZ	SCAN2
SCAN2A:
	MVI	B,3		;length of filetype field
SCAN3:
	CALL	CKLEGAL		;get and check character
	BRC	SCAN4		;name done if illegal
	CPI	' '		;end of parameter field?
	BRZ	SCAN4
	CPI	'.'		;check if another period
	BRZ	SCAN4
	CPI	'*'		;rest wild?
	BRZ	SCAN4B
	MOV	M,A
	INX	H
	BJNZ	SCAN3		;get next character
	BR	SCAN4A
SCAN4:
	INX	H		;advance to end of type field
	BJNZ	SCAN4
SCAN4A:
	CALL	INITFCB		;..and zero counter fields.
	RET
SCAN4B:
	MVI	M,'?'		;make wild
	INX	H
	BJNZ	SCAN4B
	BR	SCAN4A		;complete rest

;
; COMMAND: J
;
GOTO:
	CALL	CPRMPT
	DB	'Jump to Filename: ',0
	LXI	D,D$FCB		;pt to FCB
	CALL	FILENAME	;get file name
	LHLD	RING		;pt to first element of ring
	SHLD	RINGPOS		;set position
	SHLD	LOCBEG		;set local beginning
	PUSH	H
	LHLD	CRCTBL		;pt to CRC Table
	MVI	M,0		;set local counter
	POP	H
GOTOL:
	CALL	GOTOCOMP	;compare
	BRZ	GOTOF		;we are there
	PUSH	H
	LHLD	CRCTBL		;pt to table
	INR	M
	MOV	A,M
	POP	H
	CPI	EPS
	BRNZ	GOTOL1
	LHLD	CRCTBL		;zero first entry
	MVI	M,0
	LHLD	LOCBEG		;reset local beginning
	LXI	D,EPS*13
	DAD	D
	SHLD	LOCBEG
GOTOL1:
	LHLD	RINGPOS		;advance to next entry
	LXI	D,13
	DAD	D
	SHLD	RINGPOS		;new position
	XCHG			;position in DE
	LHLD	RINGEND		;check for completion
	CALL	CMPDEHL		;compare current position with end of ring
	BRNZ	GOTOL
	LHLD	RING		;pt to first element
	SHLD	RINGPOS		;set position
	CALL	FNF1		;print message
	JMP	CMDLOOP
GOTOF:
	LHLD	LOCBEG		;we have local beginning
	PUSH	H
	XCHG			;ring location in DE
	LXI	H,CURHOME	;set cursor ptr
	SHLD	CURAT
GOTOF0:
	LHLD	RINGPOS		;at position?
	CALL	CMPDEHL
	BRZ	GOTOF1
	LXI	H,13		;advance location
	DAD	D
	PUSH	H
	CALL	CUR$NEXT	;advance cursor
	POP	D		;pt to next ring position
	BR	GOTOF0
GOTOF1:
	POP	H		;pt to local ring
	JMP	JFW0A		;process
GOTOCOMP:
	LHLD	RINGPOS		;pt to current entry
	INX	H		;pt to first char of file name
	LXI	D,D$FCB+1	;pt to first char of new file
	MVI	B,11		;11 bytes
GOTOC1:
	LDAX	D		;get char
	CPI	'?'		;match?
	BRZ	GOTOC2
	CMP	M		;match?
	RNZ			;no match
GOTOC2:
	INX	D		;pt to next
	INX	H
	BJNZ	GOTOC1
	RET

;
; COMMAND: V
;
; Type file to console with pagination set to 'lps' -- single-line scroll
; using <space> bar , <ctrl-x> to cancel, any other key to page screen.
;
VIEW:
	CALL	VCLS
	CALL	VPRINT
	DB	CR,LF,DIM,'<CTRL-C> Cancels, <SP> Turns Up One Line, '
	DB	'Other Keys Page Screen',BRIGHT,CR,LF,LF,0
	MVI	A,1		;initialize..
	STA	LPSCNT		;..lines-per-screen counter.
	STA	VIEWFLG		; 'view' paginate if not zero
	MVI	A,WRCON		;write console out function
	BR	CURRENT		;to common i/o processing

;
; COMMAND: P
;
; Send file to logical list device -- any keypress cancels
;
LSTFILE:
	CALL	CPRMPT
	DB	'Print on LST Device (Y/N)? ',0
	CALL	KEYIN		;get response
	CPI	'Y'
	JNZ	LOOP
	CALL	ERMSG
	DB	'Printing ',0
	LHLD	RINGPOS		;pt to file name
	INX	H
	CALL	PRFN		;print it
	MVI	A,1		;one for..
	STA	VIEWFLG		;..output to printer.
	DCR	A		;zero for..
	STA	LPSCNT		;..lines-per-page counter
	MVI	A,LIST		;out to 'list' device function and fall thru

; output character for console/list/punch processing

CURRENT:
	STA	CON$LST		;save bdos function

; output file to console/printer/punch

	CALL	RINGFCB		;position name to 'fcb'
	XCHG			;HL pts to S$FCB
	CALL	INITFCB		;set 'fcb' for use
	LXI	D,TBUF		;set to use default dma buffer
	MVI	C,SETDMA	;address set function
	CALL	BDOS
	LXI	D,S$FCB		;open file for reading
	MVI	C,OPEN		;file open function code
	CALL	BDOS
	INR	A		; 0ffh --> 00h if open not okay
	BRNZ	ZEROCR		;if not okay, show error message.
;
;  File Not Found Error
;
FNF:
	CALL	FNF1		;print file not found message
	JMP	LOOP
;
;  Print File Not Found Message
;
FNF1:
	CALL	ERMSG
	DB	'File NOT Found',0
	RET
;
; Continue
;
ZEROCR:
	XRA	A
	STA	S$FCB+32	;zero file 'current record' field
	STA	CHARCNT		;zero char count for tabbing
	CALL	PHEAD		;print heading if output to LST device
READMR:
	LXI	D,S$FCB		;point at file 'fcb' for reading
	MVI	C,READ		;record read function
	CALL	BDOS
	ORA	A		;check if read okay
	BRNZ	CURDONE		;eof?
	LXI	H,TBUF		;point at record just read
	MVI	B,128		;set record character counter to output
READLP:
	MOV	A,M		;get a character
	ANI	7FH		;force to 'ascii'
	CPI	EOFCHAR		;see if end-of-file
	BRZ	CURDONE		;back to ring loop if 'eof'
	MOV	E,A		;put character for 'bdos' call
	PUSH	B
	PUSH	H
	PUSH	D		; (character in e-reg)
	LDA	CON$LST		;get function for punch/list/console output
	MOV	C,A
	MOV	A,E		;check char
	CPI	TAB		;tabulate?
	BRNZ	NOTAB
	MVI	E,' '		;space over
TABL:
	PUSH	B		;save key regs
	PUSH	D
	CALL	BDOS
	POP	D		;get key regs
	POP	B
	CALL	INCCCNT		;increment char count
	ANI	7		;check for done at every 8
	BRNZ	TABL
	BR	TABDN
NOTAB:
	CALL	BDOS		;send character
	CALL	INCCCNT		;increment char count
TABDN:
	LDA	VIEWFLG		;if 'view'..
	ORA	A
	POP	D		;get char in E in case PAGER is called
	CNZ	PAGER		;..check for 'lf'.
	MVI	E,GET		;get status or char
	MVI	C,DIRCON	;console status function
	CALL	BDOS		;status?
	POP	H
	POP	B
	ANI	7FH		;if character there, then abort..
	CNZ	CANVIEW		;already got char
	INX	H		;if not, bump buffer pointer.
	BJNZ	READLP		;no, more in present record.
	BR	READMR		;yes, get next record.
CURDONE:
	LDA	CON$LST		;console?
	CPI	WRCON
	CZ	BOTTOM		;prompt for user
	JMP	RUNSH4		;refresh screen and continue user input
PAGER:
	MOV	A,E		; (character in e-reg)
	CPI	LF
	RNZ
	XRA	A		;zero char count
	STA	CHARCNT
	LDA	CON$LST		;printer or console?
	CPI	LIST		;check for printer
	BRZ	PAGEP
	LDA	CTPP		;get number of lines of text per screen
	MOV	B,A		;... in B
	LDA	LPSCNT		;is counter..
	INR	A		;..at..
	STA	LPSCNT		;..limit..
	CMP	B		;..of lines-per-screen?
	RC			;no, return.
	XRA	A		;yes, initialize..
	STA	LPSCNT		;..for next screen full.
	CALL	VPRINT
	DB	'  ',DIM,'[View More...]',BRIGHT,CR,0	;show msg line
	CALL	DKEYIN		;wait for keyboard input
	CPI	' '		;see if <space> bar..
	PUSH	PSW
	CALL	VPRINT
	DB	'                ',CR,0	;clear above msg line
	POP	PSW
	BRNZ	CANVIEW		;..if not, see if cancel.
	LDA	CTPP		;set for single line
	DCR	A
	STA	LPSCNT		;..scroll and..
	RET			;..return for one more line.

PAGEP:
	LDA	LTPP		;get number of lines of text per page
	MOV	B,A		;... in B
	LDA	LPSCNT		;is counter..
	INR	A		;..at..
	STA	LPSCNT		;..limit..
	CMP	B		;..of lines-per-screen?
	RC			;no, return.
	XRA	A		;zero for..
	STA	LPSCNT		;..lines-per-page counter
	LDA	LFFEED		;form feed available?
	ORA	A		;0=no
	JZ	PRFEED
	CALL	LCRLF		;new line
	MVI	A,FF		;send form feed char
	CALL	LOUT
	JMP	PHEAD		;print header and done
PRFEED:
	LDA	LSPP		;number of lines to skip
	MOV	B,A		;number of lines to skip
	MVI	C,LIST		;LST output
PAGELST:
	CALL	LCRLF		;new line on LST
	BJNZ	PAGELST
	JMP	PHEAD		;print heading and done

CANVIEW:
	CPI	CTRLC		;^C?
	JZ	COMCAN
	RET			;return for another page

INCCCNT:
	LDA	CHARCNT		;increment char count
	INR	A
	STA	CHARCNT
	RET

PHEAD:
	LDA	CON$LST		;printing to printer?
	CPI	LIST
	RNZ
	LXI	H,HEADMSG	;print heading
PHEAD1:
	MOV	A,M		;get char
	ORA	A		;done?
	BRZ	PHEAD2
	CALL	LOUT		;send to printer
	INX	H		;pt to next
	BR	PHEAD1
PHEAD2:
	LXI	H,S$FCB+1	;pt to file name
	MVI	B,8		;8 chars
	CALL	PHEAD3
	MVI	A,'.'		;dot
	CALL	LOUT
	MVI	B,3		;3 more chars
	CALL	PHEAD3
	CALL	LCRLF		;new line
	CALL	LCRLF		;blank line
	RET
PHEAD3:
	MOV	A,M		;get char
	CALL	LOUT		;LST it
	INX	H		;pt to next
	BJNZ	PHEAD3
	RET

;
; COMMAND: GC
;
; Copy files tagged using the 't' command.  auto-erase if file exists
; on requested destination drive or in user area.
;
MASS$COPY:
	CALL	ERMSG
	DB	'Mass Copy',0
	XRA	A		;A=0
	STA	MASSOP		;set operation to mass copy
;
;  Main Entry Point for Mass Operation of Copy or Delete
;    On entry, MASSOP = 0 if copy, 0FFH if delete
;
MASS$RUN:
	LHLD	RINGPOS		;save position
	SHLD	SRINGPOS
	LHLD	RING
	LXI	D,-13		;back up to before first ring element
	DAD	D
	SHLD	RINGPOS		;set position
;
;  Set Flags for First Time Thru and Mass Operation
;
	XRA	A		;A=0
	STA	FIRST$M		;set for prompt for dest with first file
	DCR	A		;A=0FFH
	STA	MFLAG		;this is a mass operation
;
;  Main Mass Operation Loop
;
MASS$OLP:
	LHLD	RINGPOS		;re-entry point for next file
	LXI	D,13		;advance to next
	DAD	D
	SHLD	RINGPOS
	XCHG			;at ring..
;
;  Check for Loop Completion -- DE = Current RINGPOS
;
MASS$OLP1:
	LHLD	RINGEND		;..end yet?
	CALL	CMPDEHL		;compare present position with end
	BRZ	MASS$OLX	;done
;
;  Check for Tagged File at RINGPOS
;
	LHLD	RINGPOS		;get position
	LXI	D,12		;get 1st possible tag location
	DAD	D
	MOV	A,M		;get tag
	CPI	TAGCH
	BRNZ	MASS$OLP	;not tagged, so skip
;
;  Select Mass Operation
;
	LDA	MASSOP		;get type of mass operation
	ORA	A		;0=COPY
	BRZ	MASS$OC		;do copy
;
;  Do Mass Delete - Current File Delete
;
	CALL	DELETE		;delete filename at RINGPOS
;
;  Advance to Next File if Delete Aborted
;
	LDA	DELCODE		;deletion done?
	ORA	A		;0=no
	BRZ	MASS$OLP	;skip to next if not
;
;  Stay at Current File if Delete Done - Ring was Compressed by Delete
;
	LHLD	RINGPOS		;get current ring position
	XCHG			;... in DE (don't advance)
	BR	MASS$OLP1	;loop until thru ring list
;
;  Do Mass Copy - Current File Copy
;
MASS$OC:
	CALL	COPY		;do copy
	BR	MASS$OLP	;loop until thru ring list
;
;  Exit Mass Operation Loop
;
MASS$OLX:
	LHLD	SRINGPOS	;reset ring position
	SHLD	RINGPOS
	LDA	MASSOP		;if delete, restart
	ORA	A		;NZ=delete
	JNZ	RUNSH2
	LHLD	LOCBEG		;copy, so reset local ring
	JMP	JFW0A		;rescreen

;
; COMMAND: C
;
; Copy source file at current 'ring' position to another drive.  set-up
; fcb's and buffer area and check for correct keyboard inputs.  contains
; auto-crc file copy verification.
;
FCOPY:
;
;  Set Flags for First Time Thru and No Mass Copy
;
	XRA	A		;A=0
	STA	FIRST$M		;set for prompt for destination
	STA	MFLAG		;not a mass copy
;
;  Do Copy
;
	CALL	COPY		;do copy of file at RINGPOS
;
;  Advance to Next File
;
	JMP	FORWARD		;advance to next file
;
;  Copy File at RINGPOS
;
COPY:
	LXI	H,0		;initialize storage for..
	SHLD	CRCVAL		;..'crc' working value.
	CALL	RINGFCB		;move from 'ring' to 'sfcb'
	LXI	H,S$FCB+12	;set pointer to source extent field
	CALL	INITFCB
	MVI	B,32		;copy source 'fcb' to destination 'fcb'
	LXI	H,S$FCB+1	;from point..
	LXI	D,D$FCB+1	;..to point..
	CALL	MOVE		;..move across.
	LXI	D,S$FCB		;open file for reading
	MVI	C,OPEN		;open function
	CALL	BDOS
	INR	A		; 0ffh --> 00h if bad open
	JZ	FNF		;file not found
;
;  Source File is Open -- If first time thru, get dest dir
;
COPY2:
	LDA	FIRST$M		;by-pass prompt, drive/user compatibility..
	ORA	A		;..test, and disk reset after..
	BRNZ	COPY3M		;..1st time thru in mass-copy mode.
	DCR	A		;A=0FFH
	STA	FIRST$M		;set not first time any more
	CALL	CPRMPT		;prompt for drive selection
	DB	'Copy to DIR: ',0
	CALL	DEF$D$U
;
; Check to ensure that either drives or user areas are different
;
	LDA	FCB		;get requested drive from 'fcb' and..
	MOV	B,A		;..put into b-reg for..
	LDA	S$FCB		;..comparison
	CMP	B
	BRNZ	COPY3		;branch if different
	LDA	R$U$A		;requested user area --> rua
	MOV	B,A
	LDA	C$U$A		;current user area --> cua
	CMP	B
	BRNZ	COPY3
	CALL	ERMSG		;if not, show error condition:
	DB	'Src DIR = Dest DIR',0
	JMP	LOOP		;try again?
;
;  First File Copy - Reset System
;
COPY3:
	CALL	RESET		;make sure disk is read/write
	CALL	DLOGIN		;return home
;
;  Nth File Copy - Copy without Resetting System
;
COPY3M:
	LDA	FCB		;put requested drive into..
	STA	D$FCB		;..place in destination fcb (used to log in)
	LDA	R$U$A		;toggle to..
	CALL	SET$USR		;..requested user area.
	LXI	D,D$FCB		;search for duplicate
	MVI	C,SRCHF		; 'search first' function
	CALL	BDOS
	INR	A		;if not found, 0ffh --> 00h.  then..
	BRZ	COPY5		;go to 'make' function for new file.
	LDA	MFLAG		;auto-erase..
	ORA	A		;..if..
	BRZ	COPY4M		;..in mass-copy mode.
	CALL	ERMSG		;If found, ask to replace:
	DB	0
	LXI	H,D$FCB+1
	CALL	PRFN
	CALL	VPRINT
	DB	' Exists on Dest -- Erase (Y/N)? ',0
	CALL	KEYIN		;get answer
	CPI	'Y'		;if yes, then delete and copy ...
	BRZ	COPY4M		;... else fall thru to CUA$LOG and exit
;
;  Log into Current User Area
;
CUA$LOG:
	LDA	C$U$A		;reset to current user area
	JMP	SET$USR		;set user and return to caller

;
;  Erase destination file and proceed
;
COPY4M:
;
;  Set File Attributes
;
	LXI	H,D$FCB		;pt to FCB
	CALL	ATTRIB		;clear bytes in FCB and set attr of file
	BRZ	CUA$LOG		;return to caller if R/W not permitted
;
;  Delete old file at dest
;
	LXI	D,D$FCB		;delete file already existing
	MVI	C,ERASE		;erase function
	CALL	BDOS
;
;  Create new file at dest
;
COPY5:
	LXI	D,D$FCB		;create new file and open for writing
	MVI	C,MAKE		;make function
	CALL	BDOS
	INR	A		;if directory full, 0ffh --> 00h.
	BRNZ	COPY6		;if not, branch.
	CALL	ERMSG
	DB	'Dest Dir Full',0
	JMP	LOOPFN		;if error, back to ring processor.
;
;  Perform Copy
;
COPY6:
	CALL	CPRMPT
	DB	'Copying File ',0
	LXI	H,D$FCB+1	;print file name
	CALL	PRFNSX
	CALL	VPRINT
	DB	' to ',0
	LDA	D$FCB		;print dest DU
	ADI	'@'
	CALL	COUT		;print disk
	LDA	R$U$A		;get user
	CALL	PAFDC		;print user
	MVI	A,':'
	CALL	COUT
	XRA	A		;clear 'eof'..
	STA	EOFLAG		;..flag.
COPY6A:
	CALL	CUA$LOG		;current user area
	LXI	H,0		;clear current-record..
	SHLD	REC$CNT		;..counter.
	LHLD	BUFSTART	;set buffer start pointer..
	SHLD	BUF$PT		;..to begin pointer.
;
; read source file -- fill buffer memory or stop on 'eof' -- update 'crc'
; on-the-fly
;
COPY7:
	LHLD	BUF$PT		;set dma address to buffer pointer
	XCHG			; de-pair --> dma address
	MVI	C,SETDMA
	CALL	BDOS
	LXI	D,S$FCB		;source 'fcb' for reading
	MVI	C,READ		;record read function
	CALL	BDOS
	ORA	A		; 00h --> read okay
	BRZ	S$RD$OK
	DCR	A		;eof?
	BRZ	COPY8		;yes, end-of-file, set 'eof' flag.
	CALL	ERMSG
	DB	'Read Error',0
	JMP	LOOPFN
;
;  Read OK - Update CRC
;
S$RD$OK:
	LHLD	BUF$PT
	MVI	B,128
COPY7A:
	MOV	A,M		;get character and..
	CALL	UPDCRC		;..add to 'crc' value.
	INX	H
	DCR	B
	BRNZ	COPY7A		;loop 'till record read finished
;
;  Update Buffer Ptr and Record Count
;
	LHLD	BUF$PT		;bump buffer pointer..
	LXI	D,128		;..by..
	DAD	D		;..one..
	SHLD	BUF$PT		;..record.
	LHLD	REC$CNT		;bump buffer..
	INX	H		;..record count and..
	SHLD	REC$CNT		;..store.
	XCHG			;ready to compare to..
;
;  Check for Full Buffer
;
	LHLD	REC$MAX		;..maximum record count (full-buffer).
	CALL	CMPDEHL		;compare
	BRNZ	COPY7    	;if not full, get next record.
	BR	COPY9		;full, start first write session.
;
; Indicate end-of-file read
;
COPY8:
	MVI	A,TRUE		;set 'eof' flag
	STA	EOFLAG
;
; Write source file from memory buffer to destination
;
COPY9:
	LDA	R$U$A		;set user to requested..
	CALL	SET$USR		;..area.
	LHLD	BUFSTART	;adjust buffer pointer..
	SHLD	BUF$PT		;..to start address.
COPY10:
	LHLD	REC$CNT		;buffer empty?
	MOV	A,H
	ORA	L
	BRZ	COPY11		;buffer empty, check 'eof' flag.
	DCX	H		;dec buffer record count for each write
	SHLD	REC$CNT
	LHLD	BUF$PT		;set up dma address
	PUSH	H		;save for size bump
	XCHG			;pointer in de-pair
	MVI	C,SETDMA
	CALL	BDOS
	POP	H
	LXI	D,128		;bump pointer one record length
	DAD	D
	SHLD	BUF$PT
	LXI	D,D$FCB		;destination file 'fcb'
	MVI	C,WRITE		;write record function
	CALL	BDOS
	ORA	A   		; 00h --> write okay
	BRZ	COPY10		;okay, do next record.  else..
	CALL	ERMSG		;..say disk write error.
	DB	'Copy Disk Full',0
;
;  Error in Write -- Delete Destination File and Abort
;
C$ERA:
	LXI	D,D$FCB		;delete..
	MVI	C,ERASE		;..partial..
	CALL	BDOS		;..from directory.
	JMP	LOOPFN		;back to ring
;
;  Destination Buffer Written - Check for End
;
COPY11:
	LDA	EOFLAG		;buffer all written, check for 'eof'.
	ORA	A
	JZ	COPY6A		;branch to read next buffer full
	LXI	D,D$FCB		;point at 'fcb' for file closure
	MVI	C,CLOSE
	CALL	BDOS
	INR	A		;if no-close-error then..
	BRNZ	CRC$CMP		;..compare file crc's.
	CALL	ERMSG
	DB	'Copy Close Error',0
	JMP	C$ERA
;
; Read Destination File and Compare CRCs
;
CRC$CMP:
	LHLD	CRCVAL		;transfer 'crc' value to..
	SHLD	CRCVAL2		;..new storage area.
	LXI	H,0		;clear working storage..
	SHLD	CRCVAL		;..to continue.
	LXI	D,TBUF
	MVI	C,SETDMA
	CALL	BDOS
	LXI	H,D$FCB+12
	CALL	INITFCB
	LXI	D,D$FCB
	MVI	C,OPEN
	CALL	BDOS
	INR	A		; 0ffh --> 00h if bad open
	JZ	BADCRC		;if bad open, just say 'bad-crc'.
	XRA	A		;zero 'fcb'..
	STA	D$FCB+32	;..'cr' field.
CRCWF1:
	LXI	D,D$FCB
	MVI	C,READ
	CALL	BDOS
	ORA	A		;read okay?
	BRZ	D$RD$OK		;yes, read more.
	DCR	A		;eof?
	BRZ	FINCRC		;yes, finish up and make 'crc' comparison.
	CALL	ERMSG
	DB	'Copy Read Error',0
	JMP	LOOP
;
;  Block Read OK - Update CRC
;
D$RD$OK:
	LXI	H,TBUF
	MVI	B,128
CRCWF2:
	MOV	A,M		;get character to..
	CALL	UPDCRC		;..add to 'crc' value. 
	INX	H
	BJNZ	CRCWF2
	BR	CRCWF1
;
;  Read Complete - Check CRCs
;
FINCRC:
	LHLD	CRCVAL		;put written-file 'crc' into..
	XCHG			;..de-pair.
	LHLD	CRCVAL2		;put read-file 'crc' and..
	CALL	CMPDEHL		;..compare 'de/hl' for equality.
	BRNZ	BADCRC		;if not zero, show copy-error message.
	CALL	VPRINT		;if zero, show 'verified' message.
	DB	' -- Verified',0
	JMP	CUA$LOG		;return to current user and return to caller
;
;  Error on Copy
;
BADCRC:
	CALL	CUA$LOG		;return to current user
	CALL	ERMSG
	DB	' -- CRC Error',0
	JMP	LOOP		;abort

;
; clear attributes of file (HL) and set attributes on disk
; return code of 0FFH (NZ) indicates OK to proceed, 0 (Z) indicates abort
;
ATTRIB:
	PUSH	H		;save regs
	PUSH	H
	PUSH	D
	LHLD	DUM$FCB		;init FCB
	LXI	D,12		;12 bytes in
	DAD	D
	POP	D
	CALL	INITFCB
	LHLD	DUM$FCB		;pt to dummy FCB
	XCHG			;... in DE
	POP	H
	MVI	B,12		;copy
	PUSH	D
	CALL	MOVE
	POP	D		;pt to FCB
	MVI	C,SRCHF		;look for file
	CALL	BDOS
	INR	A		;file not found = 0
	POP	H
	RZ			;abort if no file
	PUSH	H		;save ptr
	DCR	A		;adjust
	RRC			;right 3 bits to indicate offset into BUFF
	RRC
	RRC
	LXI	H,TBUF		;pt to buffer
	ADD	L		;pt to FCB of file
	ADI	9		;pt to R/O Byte
	MOV	L,A
	MOV	A,M		;get byte
	ANI	80H		;extract R/O Bit
	BRZ	ATTRIB0
	CALL	ERMSG
	DB	0
	POP	H
	PUSH	H
	INX	H		;pt to file name
	CALL	PRFN		;print file name
	CALL	VPRINT
	DB	' is R/O -- Erase (Y/N)? ',0
	CALL	KEYIN
	CPI	'Y'
	JZ	ATTRIB0
	POP	H
	XRA	A		;error return
	RET
ATTRIB0:	
	POP	H		;get ptr
	PUSH	H		;save ptr
	INX	H		;pt to first char
	MVI	B,11		;11 Bytes
ATTRIB1:
	MOV	A,M		;get byte
	ANI	7FH		;mask it
	MOV	M,A		;put byte
	INX	H		;pt to next
	DCR	B		;count down
	JNZ	ATTRIB1
	POP	D		;pt to FCB
	MVI	C,ATTR
	CALL	BDOS
	XRA	A
	DCR	A		;no error return
	RET

;
;**************************************************
;
; CRC subroutines
;
; initialize tables for fast crc calculations

INITCRC:
	LHLD	CRCTBL
	MVI	C,0		;table index
GLOOP:
	XCHG
	LXI	H,0		;initialize crc register pair
	MOV	A,C
	PUSH	B		;save index in c-reg
	MVI	B,8
	XRA	H
	MOV	H,A
LLOOP:
	DAD	H
	BRNC	LSKIP
	MVI	A,10H		;generator is x^16 + x^12 + x^5 + x^0 as..
	XRA	H		;..recommended by ccitt for asynchronous..
	MOV	H,A		;..communications.  produces the same..
	MVI	A,21H		;..results as public domain programs..
	XRA	L		;..chek, comm7, mdm7, and modem7.
	MOV	L,A
LSKIP:
	BJNZ	LLOOP
	POP	B
	XCHG			;de-pair now has crc, hl pointing into table.
	MOV	M,D		;store high byte of crc..
	INR	H
	MOV	M,E		;..and store low byte.
	DCR	H
	INX	H		;move to next table entry
	INR	C		;next index
	BRNZ	GLOOP
	RET

UPDCRC:
	PUSH	B		;update 'crc'..
	PUSH	H		;..accumulator..
	LHLD	CRCVAL		;pick up partial remainder
	XCHG			;de-pair now has partial
	MVI	B,0
	XRA	D
	MOV	C,A
	LHLD	CRCTBL
	DAD	B
	MOV	A,M
	XRA	E
	MOV	D,A
	INR	H
	MOV	E,M
	XCHG
	SHLD	CRCVAL
	POP	H
	POP	B
	RET

;
;**************************************************
;
; WORKHORSE Routines
;
; conin routine (waits for response)
;
KEYIN:
	CALL	CIN		;get input
	CALL	CAPS		;capitalize
	JMP	COUT		;echo

; direct console input w/o echo (waits for input)

DKEYIN:
	CALL	CIN		;get char from BIOS
	JMP	CAPS		;capitalize

; convert keyboard input to upper case

CONVERT:
	LHLD	CMDBUF		; 'current keyboard buffer length'..
	INX	H
	MOV	B,M		;..to b-reg.
	MOV	A,B
	ORA	A		;if zero length, skip conversion.
	JZ	COMCAN
CONVLP:
	INX	H		;point at character to capitalize
	MOV	A,M
	CALL	CAPS
	MOV	M,A		;put back into buffer
	BJNZ	CONVLP
	RET
;
; Fill buffer with 'spaces' with count in b-reg
;
FILL:
	MVI	M,' '		;put in space character
	INX	H
	BJNZ	FILL		;no, branch.
	RET
;
; Ignore leading spaces (ls) in buffer, length in c-reg.
;
UNSPACE:
	LDAX	D		;get character
	CPI	' '
	RNZ			;not blank, a file is entered.
	INX	D		;to next character
	DCR	C
	JZ	COMCAN		;all spaces --> command recovery error
	BR	UNSPACE
;
; Check for legal filename character -- return with carry set if illegal
;
CKLEGAL:
	LDAX	D		;get character from de-pair
	INX	D		;point at next character
	CPI	' '		;less than space?
	RC			;return carry if unpermitted character
	PUSH	H
        PUSH	B
	CPI	'['		;if greater than 'z', exit with..
	BRNC	CKERR		;..carry set.
	MVI	B,CHR$TEND-CHR$TBL
	LXI	H,CHR$TBL
CHR$LP:
	CMP	M  
	BRZ	CKERR
	INX	H
	BJNZ	CHR$LP
	ORA	A		;clear carry for good character
	POP	B
	POP	H
	RET

CKERR:
	POP	B
	POP	H
	STC	     		;error exit with carry set
	RET

CHR$TBL:
	DB	',',':',';','<','=','>'	;invalid character table
CHR$TEND:
	DS	0
;
; Print file name in S$FCB
;
PRFNSX:
	PUSH	H		;save regs
	PUSH	B
	BR	PRFNS0
PRFNS:
	PUSH	H		;affect only PSW
	PUSH	B
	LXI	H,S$FCB+1
PRFNS0:
	CALL	PRFN		;print file name
	POP	B		;restore
	POP	H
	RET
;
; Print file name pted to by HL
;
PRFN:
	MVI	B,8		;8 chars
	CALL	PRFNS1
	MVI	A,'.'
	CALL	COUT
	MVI	B,3		;file type and fall thru
PRFNS1:
	MOV	A,M		;get char
	CALL	COUT
	INX	H		;pt to next
	BJNZ	PRFNS1
	RET
;
; Copy filename from RINGPOS to SFCB
;
RINGFCB:
	LHLD	RINGPOS		;move name from ring to source 'fcb'
	LXI	D,S$FCB		;place to move filename and..
	MVI	B,12		;..amount to move (fall thru to MOVE)
;
; move subroutine -- move b-reg # of bytes from hl-pair to de-pair
;
MOVE:
	MOV	A,M		;get hl-pair referenced source byte
	ANI	7FH		;strip attributes
	STAX	D		;put to de-pair referenced destination
	INX	H		;fix pointers for next search
	INX	D
	BJNZ	MOVE
	RET

MOVEB:
	PUSH	H		;SAVE HL, DE
	PUSH	D
	CALL	MOVE
	POP	D		;RESTORE DE, HL
	POP	H
	RET
;
; Initialize FCB system fields (entry with hl-pair pointing to FCB)
;
INITFCB:
	MVI	B,21		;fill ex, s1, s2, rc, cr counters with zeros.
INITLP:
	MVI	M,0		;put zero (null) in memory
	INX	H
	BJNZ	INITLP
	RET
;
; Disk system reset -- login requested drive
;
RESET:
	MVI	C,RESETDK	;reset system
	CALL	BDOS
	LDA	R$DR		;make requested drive..
SET$DR:
	MOV	E,A		;..current
	MVI	C,LOGIN
	JMP	BDOS		;return to caller
;
; Set/reset (or get) user area (call with binary user area in a-reg)
;
SET$USR:
	MOV	E,A		;user number in E
GET$USR:
	MVI	C,SGUSER
	JMP	BDOS		;return to caller
;
; Compare de-pair to hl-pair and set flags accordingly
;
CMPDEHL:
	MOV	A,D		;see if high bytes set flags
	CMP	H
	RNZ			;return if not equal
	MOV	A,E
	CMP	L		;low bytes set flags instead
	RET
;
; Shift hl-pair b-reg bits (-1) to right (divider routine)
;
SHIFTLP:
	DCR	B
	RZ
	MOV	A,H
	ORA	A
	RAR
	MOV	H,A
	MOV	A,L
	RAR
	MOV	L,A
	BR	SHIFTLP

;
; Determine free storage remaining on selected drive
;
FRESTOR:
	MVI	C,INQDISK	;determine current drive
	CALL	BDOS		;returns 0 as a:, 1 as b:, etc.
	INR	A		;make 1 --> a:, 2 --> b:, etc.
	STA	FCB
	MVI	C,GETPARM	;current disk parameter block
	CALL	BDOS
	INX	H		;bump to..
	INX	H
	MOV	A,M		;..block shift factor.
	STA	BSHIFTF		; 'bsh'
	INX	H		;bump to..
	MOV	A,M		;..block mask.
	STA	B$MASK		; 'blm'
	INX	H		;bump to..
	INX	H		;..get..
	MOV	E,M		;..maximum block number..
	INX	H		;..double..
	MOV	D,M		;..byte.
	XCHG
	SHLD	B$MAX		; 'dsm'
	MVI	C,INQALC	;address of allocation vector
	CALL	BDOS
	XCHG			;get its length
	LHLD	B$MAX
	INX	H
	LXI	B,0		;initialize block count to zero
GSPBYT:
	PUSH	D		;save allocation address
	LDAX	D
	MVI	E,8		;set to process 8 bits (blocks)
GSPLUP:
	RAL			;test bit
	BRC	NOT$FRE
	INX	B
NOT$FRE:
	MOV	D,A		;save bits
	DCX	H
	MOV	A,L
	ORA	H    
	BRZ	END$ALC		;quit if out of blocks
	MOV	A,D		;restore bits
	DCR	E		;count down 8 bits
	BRNZ	GSPLUP		;branch to do another bit
	POP	D		;bump to next count..
	INX	D		;..of allocation vector.
	BR	GSPBYT		;process it

END$ALC:
	POP	D		;clear alloc vector pointer from stack
	MOV	L,C		;copy # blocks to hl-pair
	MOV	H,B
	LDA	BSHIFTF		;get block shift factor
	SUI	3		;convert from sectors to thousands (k)
	BRZ	PRT$FRE		;skip shifts if 1k blocks
FREK$LP:
	DAD	H		;multiply blocks by k-bytes per block
	DCR	A		;multiply by 2, 4, 8, or 16.
	BRNZ	FREK$LP
PRT$FRE:
	SHLD	DISKSP		;save disk space
	RET
;
;  Print free space on disk
;
PRINT$FRE:
	CALL	ERMSG		;position and set flags
	DB	0
	LHLD	DISKSP
	CALL 	PHLFDC		; # of free k-bytes in hl-pair
	CALL	VPRINT
	DB	'K Bytes on Disk',0
	RET

;
;**************************************************
;
; MESSAGE Routines
;
; Print VFILER Banner
;
BANNER:
	CALL	VCLS		;clear screen
	LXI	H,BANADR
	CALL	GOTOXY
	CALL	VPRINT		;print banner
	DB	'VFILER, Version '
	DB	VERS/10+'0','.',(VERS MOD 10)+'0'
	 IF	Z80
	DB	'  ',DIM,'[Z80 Code]',BRIGHT
	 ELSE
	DB	'  ',DIM,'[8080 Code]',BRIGHT
	 ENDIF
	DB	0
	RET
;
; Home the Cursor
;
CUR$FIRST:
	LXI	H,CURHOME	; HOME ADDRESS
	SHLD	CURAT		; SET CURSOR POSITION
	JMP	GOTOXY
;
; Last File Position
;
CUR$LAST:
	LHLD	RINGPOS		; ADVANCE
	SHLD	LOCPOS		; SET LOCAL POSITION
CL0:
	LXI	D,13
	DAD	D
	XCHG
	LHLD	LOCEND		; END OF LOCAL RING?
	CALL	CMPDEHL
	RZ
	XCHG			; NEW POSITION
	SHLD	LOCPOS
	PUSH	H		; SAVE POSITION
	CALL	CUR$NEXT	; ADVANCE CURSOR
	POP	H		; GET POSITION
	BR	CL0
;
; Advance the Cursor
;
CUR$NEXT:
	LHLD	CURAT		; COMPUTE NEW POSITION
	MOV	A,L		; CHECK FOR NEW LINE
	ADI	19		; SIZE OF EACH ENTRY
	CPI	70
	BRNC	CN1		; ADVANCE TO NEXT LINE
	MOV	L,A		; NEW POSITION
	SHLD	CURAT
	JMP	GOTOXY
CN1:
	MOV	A,H		; GET LINE
	LXI	H,CURHOME	; GET COL
	MOV	H,A		; SET LINE AND FALL GO TO CUR$DOWN
	SHLD	CURAT
	BR	CUR$DOWN
;
; Back Up the Cursor
;
CUR$BACK:
	LXI	H,CURHOME	; GET HOME
	XCHG			; ... IN DE
	LHLD	CURAT
	CALL	CMPDEHL		; COMPARE
	BRZ	CUR$LAST	; GOTO END IF LAST
	MOV	A,L		; CHECK FOR FIRST COL
	CMP	E
	BRZ	CB1
	SUI	19		; BACK UP ONE COL
	MOV	L,A
	SHLD	CURAT		; NEW POS
	JMP	GOTOXY
CB1:
	MOV	A,E		; GET HOME COL
	ADI	19*3		; GET LAST COL
	MOV	L,A
	DCR	H		; PREV LINE
	SHLD	CURAT
	JMP	GOTOXY
;
; Move Cursor Down One Line
;
CUR$DOWN:
	LXI	H,CURHOME	; GET HOME ADDRESS
	MOV	B,H		; LINE IN B
	LHLD	CURAT		; GET CURRENT ADDRESS
	INR	H		; MOVE DOWN
	MOV	A,H		; CHECK FOR TOO FAR
	SUB	B
	CPI	EPS/4
	BRNC	CD1
	SHLD	CURAT		; OK, SO SET POSITION
	JMP	GOTOXY
CD1:
	MOV	A,L		; GET COL
	LXI	H,CURHOME
	MOV	L,A
	SHLD	CURAT
	JMP	GOTOXY
;
; Refresh Screen
;
REFRESH:
	LHLD	CURAT	; SAVE CURSOR AND RING POSITIONS
	SHLD	SCURAT
	LHLD	RINGPOS
	SHLD	SRINGPOS
	CALL	BANNER	; PRINT BANNER
;
	IF	BIHELP		;built-in help?
	LDA	HELPDISP	;DISPLAY HELP?
	ORA	A	;0=NO
	BRZ	REF1
	CALL	HELPMSG	;PRINT HELP MESSAGE
	BR	REF2
REF1:
	ENDIF		;BIHELP
;
	CALL	DISPFILES	; DISPLAY FILES
REF2:
	LXI	H,DUADR		; DU ADDRESS
	CALL	GOTOXY
	LXI	H,C$DR		; PT TO CURRENT DRIVE (FOLLOWED BY CUR USER)
	CALL	PRDU		; PRINT DU:DIR>
	LXI	H,CPMADR	; COMMAND PROMPT MESSAGE
	CALL	GOTOXY
	CALL	VPRINT		; PROMPT WITH DRIVE PREFIX
	DB	DIM
	DB	'Command (? = ',0
	LDA	HELPDISP	; IN HELP NOW?
	ORA	A		; 0=NO
	BRZ	REF3
	CALL	VPRINT
	DB	'Files',0
	BR	REF4
REF3:
	CALL	VPRINT
	DB	'Help',0
REF4:
	CALL	VPRINT
	DB	')?',BRIGHT,0
	LHLD	SCURAT		; RESTORE CURSOR AND RING POSITIONS
	SHLD	CURAT
	LHLD	SRINGPOS
	SHLD	RINGPOS
	CALL	SETCUR		; RESTORE CURSOR ON SCREEN
	RET
;
;  Print DU pted to by HL
;
PRDU:
	PUSH	H		; SAVE REGS
	PUSH	B
	MOV	A,M		; GET DRIVE
	MOV	B,A
	ADI	'A'		; CONVERT TO LETTER
	CALL	COUT
	INX	H		; PT TO USER
	MOV	A,M		; GET USER
	MOV	C,A
	CALL	PAFDC		; PRINT USER AS FLOATING
	MVI	A,':'		; PRINT COLON
	CALL	COUT
	CALL	DUTDIR		; SCAN NAMED DIRECTORY TABLE
	BRZ	PRDU2		; NO NAME?
	MVI	B,8		; PRINT NAME
PRDU1:
	MOV	A,M		; GET CHAR
	CPI	' '		; DONE IF SPACE ENCOUNTERED
	BRZ	PRDU3
	CALL	COUT
	INX	H
	BJNZ	PRDU1
	BR	PRDU3
PRDU2:
	CALL	VPRINT
	DB	'Noname',0
PRDU3:
	POP	B		; RESTORE REGS
	POP	H
	RET

;
; Refresh File Display
;
DISPFILES:
	CALL	CUR$FIRST	; POSITION CURSOR AT FIRST POSITION
	LHLD	LOCBEG		; PT TO FIRST FILE NAME
	SHLD	LOCPOS		; SAVE LOCAL POSITION
DSPF1:
	LHLD	LOCEND		; AT END?
	XCHG
	LHLD	LOCPOS
	CALL	CMPDEHL
	JZ	CUR$FIRST	; POSITION AT FIRST ENTRY AND RETURN
	MVI	B,4		; 4 SPACES
	MVI	A,' '
DSPF2:
	CALL	COUT
	BJNZ	DSPF2
	PUSH	H		; SAVE CURRENT LOCAL POSITION IN RING
	INX	H		; PT TO FILE NAME
	CALL	PRFN		; PRINT FILE NAME
	MOV	A,M		; PRINT TAG
	CALL	COUT
	POP	H		; GET CURRENT LOCAL POSITION
	LXI	D,13
	DAD	D
	SHLD	LOCPOS
	CALL	CUR$NEXT	; ADVANCE CURSOR
	BR	DSPF1
;
; Position Cursor at CURAT
;
SETCUR:
	LDA	HELPDISP	; NOGO IF IN HELP DISPLAY
	ORA	A
	RNZ
	LHLD	CURAT
	CALL	GOTOXY
	CALL	VPRINT
	DB	'-->',0
	RET
;
; Clear Cursor
;
CLRCUR:
	LDA	HELPDISP	; NOGO IF IN HELP DISPLAY
	ORA	A
	RNZ
	LHLD	CURAT
	CALL	GOTOXY
	CALL	VPRINT
	DB	'   ',0
	RET
;
; Command Prompt
;
CPRMPT:
	LXI	H,CPADR	; GET ADDRESS
MPRINT:
	PUSH	H	; SAVE ADDRESS
	CALL	GOTOXY
	PUSH	B
	MVI	B,76-(CPADR MOD 255)
	CALL	VEREOL	; ERASE TO EOL
	POP	B
	POP	H	; GET ADDRESS
	CALL	GOTOXY	; POSITION CURSOR
	JMP	VPRINT	; PRINT MESSAGE AND RETURN
;
; Working Message
;
WORKMSG:
	CALL	ERMSG
	DB	DIM,'Working ...',BRIGHT,0
	RET
;
; Error Message
;
ERMSG:
	MVI	A,0FFH	; SET ERROR MESSAGE FLAG
	STA	ERMFLG
	LXI	H,ERADR	; GET ADDRESS
	BR	MPRINT
;
; Print File Size Info
;
FSNOTE:
	CALL	ERMSG	; USE THIS ROUTINE
	DB	'Size of ',0
	RET
;
; Position for File Size Print
;
ATFS:
	LXI	H,FSADR+13	; POSITION FOR PRINT OF FILE SIZE
	JMP	GOTOXY
;
; Clear Error Message
;
ERCLR:
	XRA	A	; CLEAR FLAG
	STA	ERMFLG
	LXI	H,ERADR	; POSITION
	CALL	GOTOXY
	PUSH	B
	MVI	B,76-(ERADR MOD 255)
	CALL	VEREOL	; ERASE TO EOL
	POP	B
	RET
;
; Position at Command Prompt and Clear It
;
ATCMD:
	LXI	H,CPADR	; POSITION
	CALL	GOTOXY
	PUSH	B
	MVI	B,76-(CPADR MOD 255)
	CALL	VEREOL	; CLEAR MESSAGE
	POP	B
	LXI	H,CPADR	; REPOSITION
	JMP	GOTOXY
;
; Position at Bottom of Screen and Prompt for Continuation
;
BOTTOM:
	LXI	H,BOTADR	; POSITION
	CALL	GOTOXY
;
; Prompt for Continuation
;
SAK:
	CALL	VPRINT
	DB	DIM,'Strike Any Key -- ',BRIGHT,0
	JMP	KEYIN


;
; S T O R A G E
;
; Initialized
;
HEADMSG:
	DB	'File: ',0
MOREHELP:
	DB	'HELP '	;HELP Command for further info
	VFNAME		;VFILER Name
	DB	0
MACFCB:
	DB	0
	VFNAME		;VFILER Name
	VFNFILL		;Filler
	DB	'CMD'
FILERCMD:
	VFNAME		;VFILER Name
	VFNFILL		;Filler
	DB	' '	;one space
FILE$D:
	DB	'x'
FILE$U:
	DB	'xx'
	DB	':'	;colon
	DB	0
JOKER:
	DB	'???????????'	;*.* equivalent
FIRST$M:
	DB	FALSE		;1st time thru in mass-copy mode
MFLAG:
	DB	TRUE		;multiple file copy flag-->0 for mass copy
TAG$TOT:
	DW	0		;summation of tagged file sizes
CMDBUF:
	DS	2		;command buffer maximum length, usage, and..
;
; Uninitialized
;
STACK:
	DS	2
LWIDTH:
	DS	1	;WIDTH OF LINE
LTPP:
	DS	1	;LINES OF TEXT PER PAGE
LSPP:
	DS	1	;LINES TO SKIP PER PAGE
LFFEED:
	DS	1	;PRINTER CAN FORMFEED? (0=NO)
CWIDTH:
	DS	1	;WIDTH OF SCREEN
CTPP:
	DS	1	;LINES OF TEXT PER SCREEN
CSPP:
	DS	1	;LINES TO SKIP PER SCREEN
ALPHA:
	DS	1		;alphabetization flag (0=type and name, 0FFH=
				;...name and type)
B$MAX:
	DS	2		;highest block number on drive
B$MASK:
	DS	1		;sec/blk-1
BSHIFTF:
	DS	1		;# of shifts to multiply by sec/blk
BUFENTRY:
	DS	2		;buffer start
BUF$PT:
	DS	2		;copy buffer current pointer..
BUFSTART:
	DS	2		;..and begin pointer.
CANFLG:
	DS	1		;no-file-found cancel flag
C$DR:
	DS	1		;'current drive'
C$U$A:
	DS	1		;'current user area' (must follow C$DR)
CHARCNT:
	DS	1		;character count for tab expansion
CON$LST:
	DS	1		;bdos function storage
CRCTBL:
	DS	2		;tables for 'crc' calculations
CRCVAL:
	DS	2		;2-byte 'crc' value of working file and..
CRCVAL2:
	DS	2		;..of finished source read-file.
CURAT:
	DS	2		;current cursor position
DELCODE:
	DS	1		;deletion code (0=delete not done)
D$FCB:
	DS	33		;fcb for destination file/new name if rename
DISKSP:
	DS	2		;space remaining on disk
DUM$FCB:
	DS	2		;dummy FCB for file attributes
DRLET:
	DS	1		;scratch for drive letter
EOFLAG:
	DS	1		;file copy loop 'eof' flag
ERMFLG:
	DS	1		;error message present flag
H$DR:
	DS	1		;home drive
H$U$A:
	DS	1		;home user area (must follow H$DR)
HELPDISP:
	DS	1		;display help screen (0=no)
LPSCNT:
	DS	1		;lines-per-screen for 'view'
LOCBEG:
	DS	2		;local beginning of ring
LOCEND:
	DS	2		;local end of ring
LOCPOS:
	DS	2		;local ring position (temp)
MASSOP:
	DS	1		;mass file operation code (0=no)
MAXDR:
	DS	1		;max driver letter
MDFLG:
	DS	1		;mass delete verify flag
O$USR:
	DS	1		;store initial user area for exit
R$DR:
	DS	1		;'requested drive'
RCNT:
	DS	2		;# of records in file and..
REC$CNT:
	DS	2		;..currently in ram buffer.
REC$MAX:
	DS	2		;maximum 128-byte record capacity of buffer
RING:
	DS	2		;ptr to beginning of ring
RINGI:
	DS	2		;ring sort pointer
RINGJ:
	DS	2		;another ring sort pointer
RINGEND:
	DS	2		;current ring end pointer
RINGPOS:
	DS	2		;current ring position in scan
R$U$A:
	DS	1		;'requested user area'
SCURAT:
	DS	2		;save cursor position
S$FCB:
	DS	36		;fcb for source (random record) file
SRINGPOS:
	DS	2		;save ring position
T$DR:
	DS	1		;temp disk
TEST$RT:
	DS	1		;intermediate right-justify data
T$U$A:
	DS	1		;temp user
T$UN$FG:
	DS	1		;tag/untag file summation switch
USER:
	DS	1		;temp user buffer
VIEWFLG:
	DS	1		;00h-->to list/punch else to crt 'view'

	END

